@onekeyfe/hd-core 1.2.0-alpha.19 → 1.2.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +75 -0
- package/__tests__/DeviceCommands.test.ts +143 -7
- package/__tests__/RequestQueue.test.ts +106 -0
- package/__tests__/UploadPortfolio.test.ts +46 -0
- package/__tests__/core-dispose.test.ts +34 -0
- package/__tests__/core-error-output.test.ts +39 -0
- package/__tests__/device-lifecycle-events.test.ts +117 -0
- package/__tests__/device-pool-state.test.ts +20 -1
- package/__tests__/device-settings.test.ts +184 -6
- package/__tests__/device-state-events.test.ts +1 -2
- package/__tests__/device-state-mapper.test.ts +33 -26
- package/__tests__/device-state-store.test.ts +1 -34
- package/__tests__/device-utils.test.ts +106 -0
- package/__tests__/device-wallet-session-store.test.ts +65 -29
- package/__tests__/deviceSettings.test.ts +21 -12
- package/__tests__/evmLedgerLegacySafety.test.ts +2 -2
- package/__tests__/evmSignTypedData.test.ts +80 -0
- package/__tests__/filesystemValidation.test.ts +53 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +402 -0
- package/__tests__/get-device-state.test.ts +77 -41
- package/__tests__/logBlockEvent.test.ts +24 -11
- package/__tests__/networkUtils.test.ts +386 -0
- package/__tests__/open-wallet-session-error-response.test.ts +43 -0
- package/__tests__/open-wallet-session.test.ts +559 -0
- package/__tests__/protocol-v2.test.ts +756 -452
- package/__tests__/public-device-state-api.test.ts +113 -13
- package/__tests__/public-pro2-api-boundary.test.ts +91 -0
- package/__tests__/search-devices.test.ts +43 -0
- package/dist/api/BaseMethod.d.ts +3 -0
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
- package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
- package/dist/api/ClearSessionCache.d.ts.map +1 -1
- package/dist/api/DirList.d.ts.map +1 -1
- package/dist/api/FileRead.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +2 -2
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +3 -2
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts +14 -0
- package/dist/api/OpenWalletSession.d.ts.map +1 -0
- package/dist/api/PathInfo.d.ts.map +1 -1
- package/dist/api/PromptWebDeviceAccess.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts +3 -0
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
- package/dist/api/device/DeviceFullyUploadResource.d.ts +1 -2
- package/dist/api/device/DeviceFullyUploadResource.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceWipe.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +7 -3
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/helpers/filesystemValidation.d.ts +3 -0
- package/dist/api/helpers/filesystemValidation.d.ts.map +1 -1
- package/dist/api/helpers/paramsValidator.d.ts +1 -0
- package/dist/api/helpers/paramsValidator.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +7 -11
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -1
- package/dist/api/utils.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +17 -1
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +6 -5
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +5 -5
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts +1 -0
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts +2 -2
- package/dist/device/DeviceStateMapper.d.ts.map +1 -1
- package/dist/device/DeviceStateProjector.d.ts +1 -2
- package/dist/device/DeviceStateProjector.d.ts.map +1 -1
- package/dist/device/DeviceStateStore.d.ts +1 -3
- package/dist/device/DeviceStateStore.d.ts.map +1 -1
- package/dist/device/DeviceWalletSessionStore.d.ts +6 -1
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
- package/dist/device/cloneDeviceState.d.ts +2 -0
- package/dist/device/cloneDeviceState.d.ts.map +1 -0
- package/dist/deviceProfile/buildDeviceFeatures.d.ts +3 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +608 -75
- package/dist/index.js +2287 -1706
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/deviceSession.d.ts +7 -0
- package/dist/protocols/protocol-v2/deviceSession.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/features.d.ts +11 -4
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +16 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +8 -3
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -2
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceSettings.d.ts +1 -0
- package/dist/types/api/deviceSettings.d.ts.map +1 -1
- package/dist/types/api/deviceUnlock.d.ts +1 -1
- package/dist/types/api/deviceUnlock.d.ts.map +1 -1
- package/dist/types/api/getFeatures.d.ts.map +1 -1
- package/dist/types/api/getOnekeyFeatures.d.ts.map +1 -1
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +13 -15
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +41 -0
- package/dist/types/api/openWalletSession.d.ts.map +1 -0
- package/dist/types/api/protocolV2.d.ts +6 -11
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +2 -9
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/assets.d.ts +2 -1
- package/dist/utils/assets.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts +13 -0
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
- package/dist/utils/deviceFeaturesUtils.d.ts +11 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts +9 -8
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts +2 -0
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/deviceVersionUtils.d.ts +6 -5
- package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/networkUtils.d.ts +9 -1
- package/dist/utils/networkUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +17 -10
- package/src/api/CheckBootloaderRelease.ts +1 -2
- package/src/api/CheckFirmwareRelease.ts +1 -2
- package/src/api/ClearSessionCache.ts +0 -4
- package/src/api/DirList.ts +5 -3
- package/src/api/DirMake.ts +3 -3
- package/src/api/DirRemove.ts +3 -3
- package/src/api/FileDelete.ts +3 -3
- package/src/api/FileRead.ts +9 -6
- package/src/api/FileWrite.ts +3 -3
- package/src/api/FirmwareUpdate.ts +4 -4
- package/src/api/FirmwareUpdateV2.ts +133 -45
- package/src/api/FirmwareUpdateV3.ts +3 -5
- package/src/api/FirmwareUpdateV4.ts +192 -154
- package/src/api/GetPassphraseState.ts +10 -16
- package/src/api/OpenWalletSession.ts +221 -0
- package/src/api/PathInfo.ts +5 -3
- package/src/api/PromptWebDeviceAccess.ts +2 -3
- package/src/api/SearchDevices.ts +6 -2
- package/src/api/UploadPortfolio.ts +12 -3
- package/src/api/alephium/AlephiumSignTransaction.ts +2 -2
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +4 -8
- package/src/api/benfen/BenfenGetAddress.ts +1 -1
- package/src/api/btc/BTCGetPublicKey.ts +1 -1
- package/src/api/device/DeviceChangePin.ts +1 -0
- package/src/api/device/DeviceFullyUploadResource.ts +4 -5
- package/src/api/device/DeviceSettings.ts +93 -0
- package/src/api/device/DeviceUpdateBootloader.ts +1 -2
- package/src/api/device/DeviceWipe.ts +1 -0
- package/src/api/evm/EVMSignTypedData.ts +3 -5
- package/src/api/evm/legacyV1/getAddress.ts +2 -2
- package/src/api/evm/legacyV1/getPublicKey.ts +2 -2
- package/src/api/evm/legacyV1/signMessage.ts +2 -2
- package/src/api/evm/legacyV1/signTypedData.ts +2 -2
- package/src/api/evm/legacyV1/signTypedHash.ts +2 -2
- package/src/api/evm/legacyV1/verifyMessage.ts +2 -2
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +3 -3
- package/src/api/firmware/getBinary.ts +8 -3
- package/src/api/helpers/batchGetPublickeys.ts +2 -2
- package/src/api/helpers/filesystemValidation.ts +61 -0
- package/src/api/helpers/paramsValidator.ts +1 -1
- package/src/api/helpers/protocolV2FileWrite.ts +3 -2
- package/src/api/index.ts +9 -12
- package/src/api/nervos/NervosSignTransaction.ts +2 -2
- package/src/api/nexa/NexaSignTransaction.ts +1 -1
- package/src/api/protocol-v2/DeviceFactoryInfoGet.ts +1 -1
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
- package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +4 -4
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +6 -5
- package/src/api/protocol-v2/DeviceInfoGet.ts +4 -4
- package/src/api/protocol-v2/DeviceReboot.ts +1 -1
- package/src/api/protocol-v2/DeviceSettingsPageShow.ts +11 -8
- package/src/api/protocol-v2/DeviceSettingsSet.ts +4 -11
- package/src/api/protocol-v2/FilesystemFormat.ts +1 -1
- package/src/api/protocol-v2/FilesystemPermissionFix.ts +1 -1
- package/src/api/protocol-v2/Ping.ts +1 -1
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +1 -1
- package/src/api/utils.ts +1 -3
- package/src/core/RequestQueue.ts +107 -6
- package/src/core/index.ts +91 -46
- package/src/data/messages/messages-protocol-v2.json +25 -79
- package/src/data-manager/DataManager.ts +6 -6
- package/src/device/Device.ts +186 -102
- package/src/device/DeviceCommands.ts +51 -21
- package/src/device/DeviceFeaturesState.ts +4 -4
- package/src/device/DevicePool.ts +17 -3
- package/src/device/DeviceStateMapper.ts +13 -29
- package/src/device/DeviceStateProjector.ts +11 -8
- package/src/device/DeviceStateStore.ts +11 -52
- package/src/device/DeviceWalletSessionStore.ts +31 -4
- package/src/device/cloneDeviceState.ts +39 -0
- package/src/device/deviceIdentity.ts +2 -2
- package/src/deviceProfile/buildDeviceFeatures.ts +29 -88
- package/src/events/logBlockEvent.ts +8 -1
- package/src/inject.ts +14 -24
- package/src/protocols/protocol-v2/deviceSession.ts +24 -0
- package/src/protocols/protocol-v2/features.ts +37 -49
- package/src/protocols/protocol-v2/firmware.ts +1 -1
- package/src/protocols/protocol-v2/index.ts +2 -3
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +61 -0
- package/src/protocols/protocol-v2/uiInteraction.ts +4 -3
- package/src/protocols/protocol-v2/walletSession.ts +57 -146
- package/src/types/api/checkAllFirmwareRelease.ts +1 -2
- package/src/types/api/deviceSettings.ts +1 -0
- package/src/types/api/deviceUnlock.ts +1 -1
- package/src/types/api/firmwareUpdate.ts +5 -5
- package/src/types/api/getDeviceState.ts +1 -1
- package/src/types/api/getFeatures.ts +3 -0
- package/src/types/api/getOnekeyFeatures.ts +3 -0
- package/src/types/api/getPassphraseState.ts +6 -0
- package/src/types/api/index.ts +16 -23
- package/src/types/api/openWalletSession.ts +64 -0
- package/src/types/api/protocolV2.ts +36 -18
- package/src/types/device.ts +20 -14
- package/src/types/params.ts +1 -1
- package/src/types/settings.ts +8 -8
- package/src/utils/assets.ts +4 -1
- package/src/utils/deviceFeaturesCompat.ts +173 -0
- package/src/utils/deviceFeaturesUtils.ts +9 -8
- package/src/utils/deviceInfoUtils.ts +26 -64
- package/src/utils/deviceSettings.ts +18 -11
- package/src/utils/deviceVersionUtils.ts +25 -44
- package/src/utils/findDefectiveBatchDevice.ts +3 -3
- package/src/utils/index.ts +2 -5
- package/src/utils/networkUtils.ts +270 -14
- package/src/utils/pro2Wallpaper.ts +11 -13
- package/src/utils/tracing.ts +2 -2
- package/dist/api/internal.d.ts +0 -4
- package/dist/api/internal.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceSessionOpen.d.ts +0 -28
- package/dist/api/protocol-v2/DeviceSessionOpen.d.ts.map +0 -1
- package/src/api/internal.ts +0 -9
- package/src/api/protocol-v2/DeviceSessionOpen.ts +0 -100
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import AllNetworkGetAddressBase from '../src/api/allnetwork/AllNetworkGetAddressBase';
|
|
2
|
+
import { findMethod } from '../src/api/utils';
|
|
3
|
+
import { getActiveRequestsByDeviceInstance } from '../src/utils/tracing';
|
|
4
|
+
|
|
5
|
+
jest.mock('../src/data/config', () => ({
|
|
6
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
7
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
jest.mock('../src/api/utils', () => ({
|
|
11
|
+
findMethod: jest.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
class TestAllNetworkMethod extends AllNetworkGetAddressBase {
|
|
15
|
+
async getAllNetworkAddress() {
|
|
16
|
+
return Promise.resolve([]);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('AllNetworkGetAddressBase tracing', () => {
|
|
21
|
+
test('releases the nested request context when an unhandled error escapes', async () => {
|
|
22
|
+
const deviceInstanceId = 'device-instance';
|
|
23
|
+
const innerMethod = {
|
|
24
|
+
checkSafetyLevelOnTestNet: jest.fn().mockResolvedValue(false),
|
|
25
|
+
connectId: 'connect-id',
|
|
26
|
+
deviceId: 'device-id',
|
|
27
|
+
getVersionRange: jest.fn().mockReturnValue({}),
|
|
28
|
+
init: jest.fn(),
|
|
29
|
+
name: 'xrpGetAddress',
|
|
30
|
+
responseID: 42,
|
|
31
|
+
run: jest.fn().mockRejectedValue(new Error('address failed')),
|
|
32
|
+
setDevice: jest.fn(),
|
|
33
|
+
strictCheckDeviceSupport: false,
|
|
34
|
+
};
|
|
35
|
+
(findMethod as jest.Mock).mockReturnValue(innerMethod);
|
|
36
|
+
const method = new TestAllNetworkMethod({
|
|
37
|
+
id: 1,
|
|
38
|
+
payload: {
|
|
39
|
+
method: 'allNetworkGetAddress',
|
|
40
|
+
connectId: 'connect-id',
|
|
41
|
+
deviceId: 'device-id',
|
|
42
|
+
bundle: [],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
method.device = {
|
|
46
|
+
instanceId: deviceInstanceId,
|
|
47
|
+
getCurrentFirmwareType: jest.fn(),
|
|
48
|
+
getCurrentFirmwareVersionString: jest.fn().mockReturnValue('1.0.0'),
|
|
49
|
+
getCurrentMethodVersionRange: jest
|
|
50
|
+
.fn()
|
|
51
|
+
.mockImplementation((getRange: (type: string) => unknown) => getRange('classic')),
|
|
52
|
+
off: jest.fn(),
|
|
53
|
+
on: jest.fn(),
|
|
54
|
+
} as any;
|
|
55
|
+
|
|
56
|
+
await expect(
|
|
57
|
+
method.callMethod(
|
|
58
|
+
'xrpGetAddress',
|
|
59
|
+
{
|
|
60
|
+
bundle: [
|
|
61
|
+
{
|
|
62
|
+
_originRequestParams: {
|
|
63
|
+
network: 'xrp',
|
|
64
|
+
path: "m/44'/144'/0'/0/0",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
0
|
|
70
|
+
)
|
|
71
|
+
).rejects.toThrow('address failed');
|
|
72
|
+
|
|
73
|
+
expect(getActiveRequestsByDeviceInstance(deviceInstanceId)).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -2,6 +2,7 @@ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
|
2
2
|
|
|
3
3
|
import { DeviceCommands } from '../src/device/DeviceCommands';
|
|
4
4
|
import { DEVICE } from '../src/events';
|
|
5
|
+
import { LoggerNames, getLogger } from '../src/utils';
|
|
5
6
|
|
|
6
7
|
jest.mock('../src/data/config', () => ({
|
|
7
8
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
@@ -18,6 +19,48 @@ const createCommands = () => {
|
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
describe('DeviceCommands failure mapping', () => {
|
|
22
|
+
it('does not log DeviceSessionGet response secrets', async () => {
|
|
23
|
+
const commands = createCommands();
|
|
24
|
+
const log = getLogger(LoggerNames.DeviceCommands);
|
|
25
|
+
log.messages.length = 0;
|
|
26
|
+
|
|
27
|
+
await expect(
|
|
28
|
+
commands._filterCommonTypes(
|
|
29
|
+
{
|
|
30
|
+
type: 'DeviceSession',
|
|
31
|
+
message: {
|
|
32
|
+
session_id: 'secret-session-id',
|
|
33
|
+
btc_test_address: 'secret-wallet-address',
|
|
34
|
+
},
|
|
35
|
+
} as any,
|
|
36
|
+
'DeviceSessionGet'
|
|
37
|
+
)
|
|
38
|
+
).resolves.toMatchObject({ type: 'DeviceSession' });
|
|
39
|
+
|
|
40
|
+
expect(JSON.stringify(log.messages)).not.toContain('secret-session-id');
|
|
41
|
+
expect(JSON.stringify(log.messages)).not.toContain('secret-wallet-address');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('rejects the Pro2 bootloader DeviceStatusGet unsupported response', async () => {
|
|
45
|
+
const commands = createCommands();
|
|
46
|
+
|
|
47
|
+
await expect(
|
|
48
|
+
commands._filterCommonTypes(
|
|
49
|
+
{
|
|
50
|
+
type: 'Failure',
|
|
51
|
+
message: {
|
|
52
|
+
code: 'Failure_InvalidMessage',
|
|
53
|
+
message: 'Handler not registered',
|
|
54
|
+
},
|
|
55
|
+
} as any,
|
|
56
|
+
'DeviceStatusGet'
|
|
57
|
+
)
|
|
58
|
+
).rejects.toMatchObject({
|
|
59
|
+
errorCode: HardwareErrorCode.RuntimeError,
|
|
60
|
+
message: 'Failure_InvalidMessage,Handler not registered',
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
21
64
|
it('maps Protocol V2 DeviceLocked failure to a structured hardware error', async () => {
|
|
22
65
|
const commands = createCommands();
|
|
23
66
|
|
|
@@ -43,6 +86,97 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
43
86
|
});
|
|
44
87
|
});
|
|
45
88
|
|
|
89
|
+
it('maps Protocol V2 action-cancelled subcode to the canonical cancellation error', async () => {
|
|
90
|
+
const commands = createCommands();
|
|
91
|
+
|
|
92
|
+
await expect(
|
|
93
|
+
commands._filterCommonTypes(
|
|
94
|
+
{
|
|
95
|
+
type: 'Failure',
|
|
96
|
+
message: {
|
|
97
|
+
code: 'Failure_ProcessError',
|
|
98
|
+
subcode: 3,
|
|
99
|
+
message: 'Cancelled on device',
|
|
100
|
+
},
|
|
101
|
+
} as any,
|
|
102
|
+
'DeviceSettingsSet'
|
|
103
|
+
)
|
|
104
|
+
).rejects.toMatchObject({
|
|
105
|
+
errorCode: HardwareErrorCode.ActionCancelled,
|
|
106
|
+
params: {
|
|
107
|
+
failureCode: 'Failure_ProcessError',
|
|
108
|
+
subcode: 3,
|
|
109
|
+
firmwareMessage: 'Cancelled on device',
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it.each(['Cancelled on device', 'Confirm dismissed'])(
|
|
115
|
+
'maps legacy Protocol V2 cancellation message "%s" without a subcode',
|
|
116
|
+
async message => {
|
|
117
|
+
const commands = createCommands();
|
|
118
|
+
|
|
119
|
+
await expect(
|
|
120
|
+
commands._filterCommonTypes(
|
|
121
|
+
{
|
|
122
|
+
type: 'Failure',
|
|
123
|
+
message: {
|
|
124
|
+
code: 'Failure_ProcessError',
|
|
125
|
+
message,
|
|
126
|
+
},
|
|
127
|
+
} as any,
|
|
128
|
+
'DeviceSettingsSet'
|
|
129
|
+
)
|
|
130
|
+
).rejects.toMatchObject({
|
|
131
|
+
errorCode: HardwareErrorCode.ActionCancelled,
|
|
132
|
+
params: {
|
|
133
|
+
failureCode: 'Failure_ProcessError',
|
|
134
|
+
firmwareMessage: message,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
it('does not use the legacy cancellation-message fallback for Protocol V1', async () => {
|
|
141
|
+
const commands = createCommands();
|
|
142
|
+
(commands.device.isProtocolV2 as jest.Mock).mockReturnValue(false);
|
|
143
|
+
|
|
144
|
+
await expect(
|
|
145
|
+
commands._filterCommonTypes(
|
|
146
|
+
{
|
|
147
|
+
type: 'Failure',
|
|
148
|
+
message: {
|
|
149
|
+
code: 'Failure_ProcessError',
|
|
150
|
+
message: 'Cancelled on device',
|
|
151
|
+
},
|
|
152
|
+
} as any,
|
|
153
|
+
'ApplySettings'
|
|
154
|
+
)
|
|
155
|
+
).rejects.toMatchObject({
|
|
156
|
+
errorCode: HardwareErrorCode.RuntimeError,
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('keeps an unrecognized Protocol V2 process subcode generic', async () => {
|
|
161
|
+
const commands = createCommands();
|
|
162
|
+
|
|
163
|
+
await expect(
|
|
164
|
+
commands._filterCommonTypes(
|
|
165
|
+
{
|
|
166
|
+
type: 'Failure',
|
|
167
|
+
message: {
|
|
168
|
+
code: 'Failure_ProcessError',
|
|
169
|
+
subcode: 1,
|
|
170
|
+
message: 'Another process error',
|
|
171
|
+
},
|
|
172
|
+
} as any,
|
|
173
|
+
'DeviceSettingsPageShow'
|
|
174
|
+
)
|
|
175
|
+
).rejects.toMatchObject({
|
|
176
|
+
errorCode: HardwareErrorCode.RuntimeError,
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
46
180
|
it.each(['Device locked', 'Device is locked'])(
|
|
47
181
|
'maps legacy Protocol V2 locked message "%s" without a subcode',
|
|
48
182
|
async message => {
|
|
@@ -174,17 +308,19 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
174
308
|
describe('DeviceCommands Protocol V2 interactive response compatibility', () => {
|
|
175
309
|
it('still auto-acks ButtonRequest when no hardware UI listener is registered', async () => {
|
|
176
310
|
const commands = createCommands();
|
|
311
|
+
const emit = jest.fn();
|
|
312
|
+
const commonCall = jest.fn().mockResolvedValue({
|
|
313
|
+
type: 'Success',
|
|
314
|
+
message: {},
|
|
315
|
+
});
|
|
177
316
|
commands.device = {
|
|
178
317
|
...commands.device,
|
|
179
318
|
getCurrentDeviceType: jest.fn(() => 'pro2'),
|
|
180
319
|
setCancelableAction: jest.fn(),
|
|
181
|
-
emit
|
|
320
|
+
emit,
|
|
182
321
|
listenerCount: jest.fn(() => 0),
|
|
183
322
|
} as any;
|
|
184
|
-
commands._commonCall =
|
|
185
|
-
type: 'Success',
|
|
186
|
-
message: {},
|
|
187
|
-
}) as any;
|
|
323
|
+
commands._commonCall = commonCall as any;
|
|
188
324
|
|
|
189
325
|
await expect(
|
|
190
326
|
commands._filterCommonTypes(
|
|
@@ -196,12 +332,12 @@ describe('DeviceCommands Protocol V2 interactive response compatibility', () =>
|
|
|
196
332
|
)
|
|
197
333
|
).resolves.toMatchObject({ type: 'Success' });
|
|
198
334
|
|
|
199
|
-
expect(
|
|
335
|
+
expect(emit).toHaveBeenCalledWith(
|
|
200
336
|
DEVICE.BUTTON,
|
|
201
337
|
commands.device,
|
|
202
338
|
expect.objectContaining({ code: 'ButtonRequest_PinEntry' })
|
|
203
339
|
);
|
|
204
|
-
expect(
|
|
340
|
+
expect(commonCall).toHaveBeenCalledWith('ButtonAck', {}, undefined);
|
|
205
341
|
});
|
|
206
342
|
|
|
207
343
|
it('rejects PassphraseRequest when the Pro2 UI listener is not registered', async () => {
|
|
@@ -9,11 +9,20 @@ jest.mock('../src/data/config', () => ({
|
|
|
9
9
|
|
|
10
10
|
function buildMethod(responseID: number, operationId?: string) {
|
|
11
11
|
return {
|
|
12
|
+
connectId: 'device-1',
|
|
13
|
+
executionPriority: 'normal',
|
|
12
14
|
payload: { operationId },
|
|
13
15
|
responseID,
|
|
14
16
|
} as unknown as BaseMethod;
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
function buildBackgroundMethod(responseID: number, operationId?: string) {
|
|
20
|
+
return {
|
|
21
|
+
...buildMethod(responseID, operationId),
|
|
22
|
+
executionPriority: 'background',
|
|
23
|
+
} as unknown as BaseMethod;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
describe('RequestQueue operation cancellation', () => {
|
|
18
27
|
test('aborts only the request matching the operation id', () => {
|
|
19
28
|
const queue = new RequestQueue();
|
|
@@ -31,4 +40,101 @@ describe('RequestQueue operation cancellation', () => {
|
|
|
31
40
|
|
|
32
41
|
expect(queue.abortOperation('missing')).toBe(false);
|
|
33
42
|
});
|
|
43
|
+
|
|
44
|
+
test('honors cancellation that arrives before the request is registered', () => {
|
|
45
|
+
const queue = new RequestQueue();
|
|
46
|
+
|
|
47
|
+
expect(queue.abortOperation('portfolio-1')).toBe(false);
|
|
48
|
+
|
|
49
|
+
const portfolio = queue.createTask(buildMethod(1, 'portfolio-1'));
|
|
50
|
+
const userAction = queue.createTask(buildMethod(2, 'user-action'));
|
|
51
|
+
|
|
52
|
+
expect(portfolio.abortController?.signal.aborted).toBe(true);
|
|
53
|
+
expect(userAction.abortController?.signal.aborted).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('expires a cancellation that is never matched', () => {
|
|
57
|
+
const nowSpy = jest.spyOn(Date, 'now');
|
|
58
|
+
nowSpy.mockReturnValueOnce(1_000);
|
|
59
|
+
const queue = new RequestQueue();
|
|
60
|
+
queue.abortOperation('portfolio-1');
|
|
61
|
+
nowSpy.mockReturnValue(31_001);
|
|
62
|
+
|
|
63
|
+
const portfolio = queue.createTask(buildMethod(1, 'portfolio-1'));
|
|
64
|
+
|
|
65
|
+
expect(portfolio.abortController?.signal.aborted).toBe(false);
|
|
66
|
+
nowSpy.mockRestore();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('rejects a background request while the device has an active user request', async () => {
|
|
70
|
+
const queue = new RequestQueue();
|
|
71
|
+
queue.createTask(buildMethod(1, 'user-action'));
|
|
72
|
+
|
|
73
|
+
await expect(
|
|
74
|
+
queue.admitTask(buildBackgroundMethod(2, 'portfolio-1'), {
|
|
75
|
+
backgroundPreemptTimeoutMs: 100,
|
|
76
|
+
})
|
|
77
|
+
).resolves.toMatchObject({ status: 'busy' });
|
|
78
|
+
expect(queue.getTask(2)).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('aborts and waits for an active background request before admitting a user request', async () => {
|
|
82
|
+
const queue = new RequestQueue();
|
|
83
|
+
const portfolio = queue.createTask(buildBackgroundMethod(1, 'portfolio-1'));
|
|
84
|
+
|
|
85
|
+
const admission = queue.admitTask(buildMethod(2, 'user-action'), {
|
|
86
|
+
backgroundPreemptTimeoutMs: 100,
|
|
87
|
+
});
|
|
88
|
+
await Promise.resolve();
|
|
89
|
+
|
|
90
|
+
expect(portfolio.abortController?.signal.aborted).toBe(true);
|
|
91
|
+
expect(queue.getTask(2)).toBeUndefined();
|
|
92
|
+
|
|
93
|
+
portfolio.settled.resolve();
|
|
94
|
+
await Promise.resolve();
|
|
95
|
+
expect(queue.getTask(2)).toBeUndefined();
|
|
96
|
+
|
|
97
|
+
queue.releaseTask(1);
|
|
98
|
+
|
|
99
|
+
await expect(admission).resolves.toMatchObject({
|
|
100
|
+
status: 'reserved',
|
|
101
|
+
task: { id: 2 },
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('uses the background method abort latency when no admission override is provided', async () => {
|
|
106
|
+
const queue = new RequestQueue();
|
|
107
|
+
const portfolio = queue.createTask(buildBackgroundMethod(1, 'portfolio-1'));
|
|
108
|
+
portfolio.method.maxAbortLatencyMs = 12_000;
|
|
109
|
+
const waitForTasksReleased = jest
|
|
110
|
+
.spyOn(queue as any, 'waitForTasksReleased')
|
|
111
|
+
.mockResolvedValue(false);
|
|
112
|
+
|
|
113
|
+
await expect(queue.admitTask(buildMethod(2, 'user-action'))).resolves.toMatchObject({
|
|
114
|
+
status: 'busy',
|
|
115
|
+
});
|
|
116
|
+
expect(waitForTasksReleased).toHaveBeenCalledWith([portfolio], 12_000);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('returns busy when an aborted background request does not release in time', async () => {
|
|
120
|
+
const queue = new RequestQueue();
|
|
121
|
+
const portfolio = queue.createTask(buildBackgroundMethod(1, 'portfolio-1'));
|
|
122
|
+
|
|
123
|
+
await expect(
|
|
124
|
+
queue.admitTask(buildMethod(2, 'user-action'), {
|
|
125
|
+
backgroundPreemptTimeoutMs: 1,
|
|
126
|
+
})
|
|
127
|
+
).resolves.toMatchObject({
|
|
128
|
+
status: 'busy',
|
|
129
|
+
blockingTask: { id: 1 },
|
|
130
|
+
});
|
|
131
|
+
expect(portfolio.abortController?.signal.aborted).toBe(true);
|
|
132
|
+
expect(queue.getTask(2)).toBeUndefined();
|
|
133
|
+
|
|
134
|
+
queue.releaseTask(1);
|
|
135
|
+
await expect(queue.admitTask(buildMethod(3, 'user-retry'))).resolves.toMatchObject({
|
|
136
|
+
status: 'reserved',
|
|
137
|
+
task: { id: 3 },
|
|
138
|
+
});
|
|
139
|
+
});
|
|
34
140
|
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import UploadPortfolio from '../src/api/UploadPortfolio';
|
|
2
|
+
|
|
3
|
+
jest.mock('../src/data/config', () => ({
|
|
4
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
5
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
describe('UploadPortfolio timeout', () => {
|
|
9
|
+
test('applies the command timeout to staging and PortfolioUpdate', async () => {
|
|
10
|
+
const packageBytes = new Uint8Array([1, 2, 3]);
|
|
11
|
+
const typedCall = jest
|
|
12
|
+
.fn()
|
|
13
|
+
.mockResolvedValueOnce({ message: { processed_byte: 3 } })
|
|
14
|
+
.mockResolvedValueOnce({ message: {} });
|
|
15
|
+
const method = new UploadPortfolio({
|
|
16
|
+
id: 1,
|
|
17
|
+
payload: {
|
|
18
|
+
method: 'uploadPortfolio',
|
|
19
|
+
packageBytes,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
method.device = {
|
|
23
|
+
commands: { typedCall },
|
|
24
|
+
} as any;
|
|
25
|
+
method.postMessage = jest.fn();
|
|
26
|
+
|
|
27
|
+
method.init();
|
|
28
|
+
await method.run();
|
|
29
|
+
|
|
30
|
+
expect(method.executionPriority).toBe('background');
|
|
31
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
32
|
+
1,
|
|
33
|
+
'FilesystemFileWrite',
|
|
34
|
+
'FilesystemFile',
|
|
35
|
+
expect.any(Object),
|
|
36
|
+
{ timeoutMs: 5_000 }
|
|
37
|
+
);
|
|
38
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
39
|
+
2,
|
|
40
|
+
'PortfolioUpdate',
|
|
41
|
+
'Success',
|
|
42
|
+
{},
|
|
43
|
+
{ timeoutMs: 5_000 }
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import Core, { initConnector } from '../src/core';
|
|
2
|
+
import TransportManager from '../src/data-manager/TransportManager';
|
|
3
|
+
import { DevicePool } from '../src/device/DevicePool';
|
|
4
|
+
|
|
5
|
+
jest.mock('../src/data/config', () => ({
|
|
6
|
+
getSDKVersion: jest.fn(() => '1.0.0-test'),
|
|
7
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0-test/',
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe('Core.dispose', () => {
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
jest.restoreAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('幂等停止 Connector 和 Transport,并重置共享设备状态', async () => {
|
|
16
|
+
const connector = initConnector();
|
|
17
|
+
const connectorStop = jest.spyOn(connector, 'stop');
|
|
18
|
+
const transportStop = jest.fn().mockResolvedValue(undefined);
|
|
19
|
+
jest.spyOn(TransportManager, 'getTransport').mockReturnValue({ stop: transportStop } as never);
|
|
20
|
+
const resetDevicePool = jest.spyOn(DevicePool, 'resetState');
|
|
21
|
+
const core = new Core();
|
|
22
|
+
|
|
23
|
+
const firstDispose = core.dispose();
|
|
24
|
+
expect(connectorStop).toHaveBeenCalledTimes(1);
|
|
25
|
+
expect(transportStop).toHaveBeenCalledTimes(1);
|
|
26
|
+
expect(resetDevicePool).toHaveBeenCalledTimes(1);
|
|
27
|
+
|
|
28
|
+
await firstDispose;
|
|
29
|
+
await core.dispose();
|
|
30
|
+
expect(connectorStop).toHaveBeenCalledTimes(1);
|
|
31
|
+
expect(transportStop).toHaveBeenCalledTimes(1);
|
|
32
|
+
expect(resetDevicePool).toHaveBeenCalledTimes(1);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { initCore } from '../src/core';
|
|
2
|
+
import { IFRAME } from '../src/events';
|
|
3
|
+
|
|
4
|
+
jest.mock('../src/data/config', () => ({
|
|
5
|
+
getSDKVersion: jest.fn(() => '1.0.0-test'),
|
|
6
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0-test/',
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
jest.mock('../src/data-manager/TransportManager', () => ({
|
|
10
|
+
__esModule: true,
|
|
11
|
+
default: {
|
|
12
|
+
configure: jest.fn().mockResolvedValue(undefined),
|
|
13
|
+
getTransport: jest.fn(() => undefined),
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
describe('Core 错误输出边界', () => {
|
|
18
|
+
test('连接失败只返回结构化错误,不直接写入 stdout', async () => {
|
|
19
|
+
const stdout = jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
20
|
+
const core = initCore();
|
|
21
|
+
|
|
22
|
+
const response = await core.handleMessage({
|
|
23
|
+
id: 1,
|
|
24
|
+
event: IFRAME.CALL,
|
|
25
|
+
type: IFRAME.CALL,
|
|
26
|
+
payload: {
|
|
27
|
+
method: 'getDeviceState',
|
|
28
|
+
connectId: 'missing-device',
|
|
29
|
+
retryCount: 0,
|
|
30
|
+
pollIntervalTime: 1,
|
|
31
|
+
timeout: 10,
|
|
32
|
+
},
|
|
33
|
+
} as never);
|
|
34
|
+
|
|
35
|
+
expect(response).toMatchObject({ success: false });
|
|
36
|
+
expect(stdout).not.toHaveBeenCalled();
|
|
37
|
+
await core.dispose();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { initConnector, initCore } from '../src/core';
|
|
2
|
+
import { DataManager } from '../src/data-manager';
|
|
3
|
+
import TransportManager from '../src/data-manager/TransportManager';
|
|
4
|
+
import { Device } from '../src/device/Device';
|
|
5
|
+
import { DevicePool } from '../src/device/DevicePool';
|
|
6
|
+
import { CORE_EVENT, DEVICE, IFRAME } from '../src/events';
|
|
7
|
+
|
|
8
|
+
import type Core from '../src/core';
|
|
9
|
+
import type { CoreMessage } from '../src/events';
|
|
10
|
+
import type { KnownDevice } from '../src/types';
|
|
11
|
+
|
|
12
|
+
jest.mock('../src/data/config', () => ({
|
|
13
|
+
getSDKVersion: jest.fn(() => '1.0.0-test'),
|
|
14
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0-test/',
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const createInitializedDevice = (protocol: 'V1' | 'V2') => {
|
|
18
|
+
const device = Device.fromDescriptor({
|
|
19
|
+
id: 'ble-discovery-id',
|
|
20
|
+
path: 'ble-discovery-id',
|
|
21
|
+
commType: 'ble',
|
|
22
|
+
protocolType: protocol,
|
|
23
|
+
} as never);
|
|
24
|
+
device.mainId = 'ble-connect-id';
|
|
25
|
+
device.features = {
|
|
26
|
+
protocol,
|
|
27
|
+
deviceType: protocol === 'V2' ? 'pro2' : 'classic',
|
|
28
|
+
firmwareType: 'universal',
|
|
29
|
+
deviceId: 'wallet-device-id',
|
|
30
|
+
serialNo: 'SERIAL-001',
|
|
31
|
+
label: 'OneKey Test',
|
|
32
|
+
bleName: 'OneKey Test BLE',
|
|
33
|
+
capabilities: [],
|
|
34
|
+
mode: 'normal',
|
|
35
|
+
initialized: true,
|
|
36
|
+
bootloaderMode: false,
|
|
37
|
+
firmwareVersion: '1.2.3',
|
|
38
|
+
bootloaderVersion: '1.0.0',
|
|
39
|
+
boardVersion: '1.0.0',
|
|
40
|
+
bleVersion: '2.3.4',
|
|
41
|
+
} as never;
|
|
42
|
+
return device;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
describe('public device lifecycle events', () => {
|
|
46
|
+
let core: Core | undefined;
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
DevicePool.resetState();
|
|
50
|
+
jest.spyOn(TransportManager, 'load').mockImplementation();
|
|
51
|
+
TransportManager.transport = {
|
|
52
|
+
stop: jest.fn().mockResolvedValue(undefined),
|
|
53
|
+
} as never;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterEach(async () => {
|
|
57
|
+
await core?.dispose();
|
|
58
|
+
core = undefined;
|
|
59
|
+
jest.restoreAllMocks();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test.each([
|
|
63
|
+
['react-native', 'V1'],
|
|
64
|
+
['react-native', 'V2'],
|
|
65
|
+
['lowlevel', 'V1'],
|
|
66
|
+
['lowlevel', 'V2'],
|
|
67
|
+
] as const)(
|
|
68
|
+
'emits KnownDevice snapshots for %s Protocol %s connect and disconnect events',
|
|
69
|
+
async (env, protocol) => {
|
|
70
|
+
jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
|
|
71
|
+
core = initCore();
|
|
72
|
+
initConnector();
|
|
73
|
+
|
|
74
|
+
await core.handleMessage({
|
|
75
|
+
id: 'register-device-connect-listener',
|
|
76
|
+
event: IFRAME.CALL,
|
|
77
|
+
type: IFRAME.CALL,
|
|
78
|
+
payload: { method: 'clearSessionCache' },
|
|
79
|
+
} as CoreMessage);
|
|
80
|
+
|
|
81
|
+
const messages: CoreMessage[] = [];
|
|
82
|
+
core.on(CORE_EVENT, message => messages.push(message));
|
|
83
|
+
const internalDevice = createInitializedDevice(protocol);
|
|
84
|
+
|
|
85
|
+
DevicePool.emitter.emit(DEVICE.CONNECT, internalDevice);
|
|
86
|
+
internalDevice.updateState({ identity: { label: 'Updated label' } }, 'apply-settings');
|
|
87
|
+
DevicePool.emitter.emit(DEVICE.DISCONNECT, internalDevice);
|
|
88
|
+
|
|
89
|
+
const connectMessage = messages.find(message => message.type === DEVICE.CONNECT);
|
|
90
|
+
const disconnectMessage = messages.find(message => message.type === DEVICE.DISCONNECT);
|
|
91
|
+
const connectedDevice = connectMessage?.payload.device as KnownDevice;
|
|
92
|
+
const disconnectedDevice = disconnectMessage?.payload.device as KnownDevice;
|
|
93
|
+
|
|
94
|
+
expect(connectedDevice).toMatchObject({
|
|
95
|
+
connectId: 'ble-connect-id',
|
|
96
|
+
serialNo: 'SERIAL-001',
|
|
97
|
+
uuid: 'SERIAL-001',
|
|
98
|
+
label: 'OneKey Test',
|
|
99
|
+
state: { protocol },
|
|
100
|
+
});
|
|
101
|
+
expect(disconnectedDevice).toMatchObject({
|
|
102
|
+
connectId: 'ble-connect-id',
|
|
103
|
+
serialNo: 'SERIAL-001',
|
|
104
|
+
uuid: 'SERIAL-001',
|
|
105
|
+
label: 'Updated label',
|
|
106
|
+
state: { protocol },
|
|
107
|
+
});
|
|
108
|
+
expect(connectedDevice).not.toBe(internalDevice);
|
|
109
|
+
expect(connectedDevice).not.toBe(disconnectedDevice);
|
|
110
|
+
expect(connectedDevice).not.toHaveProperty('run');
|
|
111
|
+
expect(connectedDevice).not.toHaveProperty('acquire');
|
|
112
|
+
expect(connectedDevice).not.toHaveProperty('commands');
|
|
113
|
+
expect(() => JSON.stringify(connectedDevice)).not.toThrow();
|
|
114
|
+
expect(connectedDevice.label).toBe('OneKey Test');
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
});
|
|
@@ -34,7 +34,26 @@ describe('DevicePool state lifecycle', () => {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
expect(device.run).toHaveBeenCalledTimes(1);
|
|
37
|
-
expect(getDeviceState).
|
|
37
|
+
expect(getDeviceState).toHaveBeenNthCalledWith(1, { refreshSections: ['status'] });
|
|
38
|
+
expect(getDeviceState).toHaveBeenNthCalledWith(2, { refreshSections: ['settings'] });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('keeps a discovered device when its Protocol V2 label cannot be read', async () => {
|
|
42
|
+
const labelError = new Error('settings unavailable');
|
|
43
|
+
const getDeviceState = jest
|
|
44
|
+
.fn()
|
|
45
|
+
.mockResolvedValueOnce({ status: { mode: 'normal' } })
|
|
46
|
+
.mockRejectedValueOnce(labelError);
|
|
47
|
+
const device = {
|
|
48
|
+
isProtocolV2: () => true,
|
|
49
|
+
getDeviceState,
|
|
50
|
+
run: jest.fn(async (callback: () => Promise<void>) => callback()),
|
|
51
|
+
} as any;
|
|
52
|
+
|
|
53
|
+
await expect(
|
|
54
|
+
(DevicePool as any)._refreshRuntimeState(device, { refreshRuntimeState: true })
|
|
55
|
+
).resolves.toBeUndefined();
|
|
56
|
+
expect(getDeviceState).toHaveBeenCalledTimes(2);
|
|
38
57
|
});
|
|
39
58
|
|
|
40
59
|
test('uses the disconnected descriptor and invalidates its cached state', () => {
|