mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-21 17:37:01 +01:00
Capture exception with sentry when invariant conditions are met in migrations (#20427)
* capture exception for sentry when invariant conditions are met in migration 82 * Code cleanup * Capture exceptions in invariant conditions for migrations 83,84,85,86,89,91,93,94 * Update app/scripts/migrations/082.test.js Co-authored-by: Mark Stacey <markjstacey@gmail.com> * Code cleanup * Fix SentryObject type declaration * Stop throwing error if preferences controller is undefined * Refactor 084 and 086 to remove double negative * Capture exceptions for invariant states in in migrations 87,88,90 and 92 * lint fix * log warning in migration 82 when preferences controller is undefined --------- Co-authored-by: Mark Stacey <markjstacey@gmail.com>
This commit is contained in:
parent
5cbfa82018
commit
b874a301f5
@ -1,6 +1,12 @@
|
|||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { migrate, version } from './082';
|
import { migrate, version } from './082';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('uuid', () => {
|
jest.mock('uuid', () => {
|
||||||
const actual = jest.requireActual('uuid');
|
const actual = jest.requireActual('uuid');
|
||||||
|
|
||||||
@ -472,10 +478,72 @@ describe('migration #82', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
const newStorage = await migrate(oldStorage);
|
const newStorage = await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalled();
|
||||||
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not change anything if there is no frequentRpcListDetail property on PreferencesController', async () => {
|
it('should capture an exception if any PreferencesController.frequentRpcListDetail entries are not objects', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 81,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
PreferencesController: {
|
||||||
|
transactionSecurityCheckEnabled: false,
|
||||||
|
useBlockie: false,
|
||||||
|
useCurrencyRateCheck: true,
|
||||||
|
useMultiAccountBalanceChecker: true,
|
||||||
|
useNftDetection: false,
|
||||||
|
useNonceField: false,
|
||||||
|
frequentRpcListDetail: [
|
||||||
|
{
|
||||||
|
chainId: '0x539',
|
||||||
|
nickname: 'Localhost 8545',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl: 'http://localhost:8545',
|
||||||
|
ticker: 'ETH',
|
||||||
|
},
|
||||||
|
'invalid entry type',
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
NetworkController: {
|
||||||
|
network: '1',
|
||||||
|
networkDetails: {
|
||||||
|
EIPS: {
|
||||||
|
1559: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previousProviderStore: {
|
||||||
|
chainId: '0x89',
|
||||||
|
nickname: 'Polygon Mainnet',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl:
|
||||||
|
'https://polygon-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
|
||||||
|
ticker: 'MATIC',
|
||||||
|
type: 'rpc',
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
chainId: '0x1',
|
||||||
|
nickname: '',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl: '',
|
||||||
|
ticker: 'ETH',
|
||||||
|
type: 'mainnet',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(
|
||||||
|
`state.PreferencesController.frequentRpcListDetail contains an element of type string`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change anything, and not capture an exception, if there is no frequentRpcListDetail property on PreferencesController but there is a networkConfigurations object', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
version: 81,
|
version: 81,
|
||||||
@ -556,9 +624,60 @@ describe('migration #82', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
const newStorage = await migrate(oldStorage);
|
const newStorage = await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).not.toHaveBeenCalled();
|
||||||
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no frequentRpcListDetail property on PreferencesController and no networkConfiguration object', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 81,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
PreferencesController: {
|
||||||
|
transactionSecurityCheckEnabled: false,
|
||||||
|
useBlockie: false,
|
||||||
|
useCurrencyRateCheck: true,
|
||||||
|
useMultiAccountBalanceChecker: true,
|
||||||
|
useNftDetection: false,
|
||||||
|
useNonceField: false,
|
||||||
|
},
|
||||||
|
NetworkController: {
|
||||||
|
network: '1',
|
||||||
|
networkDetails: {
|
||||||
|
EIPS: {
|
||||||
|
1559: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previousProviderStore: {
|
||||||
|
chainId: '0x89',
|
||||||
|
nickname: 'Polygon Mainnet',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl:
|
||||||
|
'https://polygon-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
|
||||||
|
ticker: 'MATIC',
|
||||||
|
type: 'rpc',
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
chainId: '0x1',
|
||||||
|
nickname: '',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl: '',
|
||||||
|
ticker: 'ETH',
|
||||||
|
type: 'mainnet',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(
|
||||||
|
`typeof state.PreferencesController.frequentRpcListDetail is undefined`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should change nothing if PreferencesController is undefined', async () => {
|
it('should change nothing if PreferencesController is undefined', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -595,4 +714,61 @@ describe('migration #82', () => {
|
|||||||
const newStorage = await migrate(oldStorage);
|
const newStorage = await migrate(oldStorage);
|
||||||
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if PreferencesController is not an object', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 81,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
network: '1',
|
||||||
|
networkDetails: {
|
||||||
|
EIPS: {
|
||||||
|
1559: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previousProviderStore: {
|
||||||
|
chainId: '0x89',
|
||||||
|
nickname: 'Polygon Mainnet',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl:
|
||||||
|
'https://polygon-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
|
||||||
|
ticker: 'MATIC',
|
||||||
|
type: 'rpc',
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
chainId: '0x1',
|
||||||
|
nickname: '',
|
||||||
|
rpcPrefs: {},
|
||||||
|
rpcUrl: '',
|
||||||
|
ticker: 'ETH',
|
||||||
|
type: 'mainnet',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreferencesController: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.PreferencesController is boolean`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if NetworkController is undefined', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 81,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
PreferencesController: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { hasProperty, isObject } from '@metamask/utils';
|
import { hasProperty, isObject } from '@metamask/utils';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
import log from 'loglevel';
|
||||||
|
|
||||||
export const version = 82;
|
export const version = 82;
|
||||||
|
|
||||||
@ -25,14 +26,56 @@ export async function migrate(originalVersionedData: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transformState(state: Record<string, unknown>) {
|
function transformState(state: Record<string, unknown>) {
|
||||||
|
if (!hasProperty(state, 'PreferencesController')) {
|
||||||
|
log.warn(`state.PreferencesController is undefined`);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
if (!isObject(state.PreferencesController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.PreferencesController is ${typeof state.PreferencesController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
!hasProperty(state, 'PreferencesController') ||
|
!hasProperty(state, 'NetworkController') ||
|
||||||
!isObject(state.PreferencesController) ||
|
!isObject(state.NetworkController)
|
||||||
!isObject(state.NetworkController) ||
|
|
||||||
!hasProperty(state.PreferencesController, 'frequentRpcListDetail') ||
|
|
||||||
!Array.isArray(state.PreferencesController.frequentRpcListDetail) ||
|
|
||||||
!state.PreferencesController.frequentRpcListDetail.every(isObject)
|
|
||||||
) {
|
) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!hasProperty(state.PreferencesController, 'frequentRpcListDetail') ||
|
||||||
|
!Array.isArray(state.PreferencesController.frequentRpcListDetail)
|
||||||
|
) {
|
||||||
|
const inPost077SupplementFor082State =
|
||||||
|
state.NetworkController.networkConfigurations &&
|
||||||
|
state.PreferencesController.frequentRpcListDetail === undefined;
|
||||||
|
if (!inPost077SupplementFor082State) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.PreferencesController.frequentRpcListDetail is ${typeof state
|
||||||
|
.PreferencesController.frequentRpcListDetail}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
if (!state.PreferencesController.frequentRpcListDetail.every(isObject)) {
|
||||||
|
const erroneousElement =
|
||||||
|
state.PreferencesController.frequentRpcListDetail.find(
|
||||||
|
(element) => !isObject(element),
|
||||||
|
);
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`state.PreferencesController.frequentRpcListDetail contains an element of type ${typeof erroneousElement}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
const { PreferencesController, NetworkController } = state;
|
const { PreferencesController, NetworkController } = state;
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { migrate, version } from './083';
|
import { migrate, version } from './083';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('uuid', () => {
|
jest.mock('uuid', () => {
|
||||||
const actual = jest.requireActual('uuid');
|
const actual = jest.requireActual('uuid');
|
||||||
|
|
||||||
@ -165,6 +171,24 @@ describe('migration #83', () => {
|
|||||||
expect(newStorage).toStrictEqual(expectedNewStorage);
|
expect(newStorage).toStrictEqual(expectedNewStorage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if state.NetworkController is undefined', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not modify state if state.NetworkController is not an object', async () => {
|
it('should not modify state if state.NetworkController is not an object', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -190,6 +214,25 @@ describe('migration #83', () => {
|
|||||||
expect(newStorage).toStrictEqual(expectedNewStorage);
|
expect(newStorage).toStrictEqual(expectedNewStorage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if state.NetworkController is not an object', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: false,
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is boolean`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not modify state if state.NetworkController.networkConfigurations is undefined', async () => {
|
it('should not modify state if state.NetworkController.networkConfigurations is undefined', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -221,6 +264,28 @@ describe('migration #83', () => {
|
|||||||
expect(newStorage).toStrictEqual(expectedNewStorage);
|
expect(newStorage).toStrictEqual(expectedNewStorage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if state.NetworkController.networkConfigurations is undefined', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
testNetworkControllerProperty: 'testNetworkControllerValue',
|
||||||
|
networkConfigurations: undefined,
|
||||||
|
},
|
||||||
|
testProperty: 'testValue',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof NetworkController.networkConfigurations is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not modify state if state.NetworkController.networkConfigurations is an empty object', async () => {
|
it('should not modify state if state.NetworkController.networkConfigurations is an empty object', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
|
@ -25,11 +25,21 @@ export async function migrate(originalVersionedData: {
|
|||||||
|
|
||||||
function transformState(state: Record<string, unknown>) {
|
function transformState(state: Record<string, unknown>) {
|
||||||
if (!isObject(state.NetworkController)) {
|
if (!isObject(state.NetworkController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
const { NetworkController } = state;
|
const { NetworkController } = state;
|
||||||
|
|
||||||
if (!isObject(NetworkController.networkConfigurations)) {
|
if (!isObject(NetworkController.networkConfigurations)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof NetworkController.networkConfigurations is ${typeof NetworkController.networkConfigurations}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
import { migrate } from './084';
|
import { migrate } from './084';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
describe('migration #84', () => {
|
describe('migration #84', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('updates the version metadata', async () => {
|
it('updates the version metadata', async () => {
|
||||||
const originalVersionedData = buildOriginalVersionedData({
|
const originalVersionedData = buildOriginalVersionedData({
|
||||||
meta: {
|
meta: {
|
||||||
@ -27,6 +37,21 @@ describe('migration #84', () => {
|
|||||||
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the network controller state does not exist', async () => {
|
||||||
|
const originalVersionedData = buildOriginalVersionedData({
|
||||||
|
data: {
|
||||||
|
test: '123',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await migrate(originalVersionedData);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const nonObjects = [undefined, null, 'test', 1, ['test']];
|
const nonObjects = [undefined, null, 'test', 1, ['test']];
|
||||||
for (const invalidState of nonObjects) {
|
for (const invalidState of nonObjects) {
|
||||||
it(`does not change the state if the network controller state is ${invalidState}`, async () => {
|
it(`does not change the state if the network controller state is ${invalidState}`, async () => {
|
||||||
@ -40,6 +65,21 @@ describe('migration #84', () => {
|
|||||||
|
|
||||||
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`captures an exception if the network controller state is ${invalidState}`, async () => {
|
||||||
|
const originalVersionedData = buildOriginalVersionedData({
|
||||||
|
data: {
|
||||||
|
NetworkController: invalidState,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await migrate(originalVersionedData);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is ${typeof invalidState}`),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('does not change the state if the network controller state does not include "network"', async () => {
|
it('does not change the state if the network controller state does not include "network"', async () => {
|
||||||
@ -56,6 +96,38 @@ describe('migration #84', () => {
|
|||||||
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the network controller state does not include "network" and does not include "networkId"', async () => {
|
||||||
|
const originalVersionedData = buildOriginalVersionedData({
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
test: '123',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await migrate(originalVersionedData);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController.network is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not capture an exception if the network controller state does not include "network" but does include "networkId"', async () => {
|
||||||
|
const originalVersionedData = buildOriginalVersionedData({
|
||||||
|
data: {
|
||||||
|
NetworkController: {
|
||||||
|
test: '123',
|
||||||
|
networkId: 'foobar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await migrate(originalVersionedData);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('replaces "network" in the network controller state with "networkId": null, "networkStatus": "unknown" if it is "loading"', async () => {
|
it('replaces "network" in the network controller state with "networkId": null, "networkStatus": "unknown" if it is "loading"', async () => {
|
||||||
const originalVersionedData = buildOriginalVersionedData({
|
const originalVersionedData = buildOriginalVersionedData({
|
||||||
data: {
|
data: {
|
||||||
|
@ -25,9 +25,26 @@ export async function migrate(originalVersionedData: {
|
|||||||
function transformState(state: Record<string, unknown>) {
|
function transformState(state: Record<string, unknown>) {
|
||||||
if (
|
if (
|
||||||
!hasProperty(state, 'NetworkController') ||
|
!hasProperty(state, 'NetworkController') ||
|
||||||
!isObject(state.NetworkController) ||
|
!isObject(state.NetworkController)
|
||||||
!hasProperty(state.NetworkController, 'network')
|
|
||||||
) {
|
) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
if (!hasProperty(state.NetworkController, 'network')) {
|
||||||
|
const thePost077SupplementFor084HasNotModifiedState =
|
||||||
|
state.NetworkController.networkId === undefined;
|
||||||
|
if (thePost077SupplementFor084HasNotModifiedState) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.network is ${typeof state
|
||||||
|
.NetworkController.network}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { migrate, version } from './085';
|
import { migrate, version } from './085';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('uuid', () => {
|
jest.mock('uuid', () => {
|
||||||
const actual = jest.requireActual('uuid');
|
const actual = jest.requireActual('uuid');
|
||||||
|
|
||||||
@ -10,6 +16,10 @@ jest.mock('uuid', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('migration #85', () => {
|
describe('migration #85', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should update the version metadata', async () => {
|
it('should update the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -39,6 +49,25 @@ describe('migration #85', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception there is no network controller state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 84,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is no network controller previous provider state', async () => {
|
it('should return state unaltered if there is no network controller previous provider state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
|
@ -24,6 +24,11 @@ export async function migrate(originalVersionedData: {
|
|||||||
|
|
||||||
function transformState(state: Record<string, unknown>) {
|
function transformState(state: Record<string, unknown>) {
|
||||||
if (!isObject(state.NetworkController)) {
|
if (!isObject(state.NetworkController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { migrate, version } from './086';
|
import { migrate, version } from './086';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('uuid', () => {
|
jest.mock('uuid', () => {
|
||||||
const actual = jest.requireActual('uuid');
|
const actual = jest.requireActual('uuid');
|
||||||
|
|
||||||
@ -10,6 +16,10 @@ jest.mock('uuid', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('migration #86', () => {
|
describe('migration #86', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should update the version metadata', async () => {
|
it('should update the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -39,6 +49,25 @@ describe('migration #86', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 85,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is no network controller provider state', async () => {
|
it('should return state unaltered if there is no network controller provider state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
@ -59,6 +88,52 @@ describe('migration #86', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller provider state and no providerConfig state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
networkConfigurations: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 85,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController.provider is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not capture an exception if there is no network controller provider state but there is a providerConfig state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
networkConfigurations: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
providerConfig: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 85,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should rename the provider config state', async () => {
|
it('should rename the provider config state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
|
@ -37,5 +37,24 @@ function transformState(state: Record<string, unknown>) {
|
|||||||
NetworkController: networkControllerState,
|
NetworkController: networkControllerState,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (!isObject(state.NetworkController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (!hasProperty(state.NetworkController, 'provider')) {
|
||||||
|
const thePost077SupplementFor086HasNotModifiedState =
|
||||||
|
state.NetworkController.providerConfig === undefined;
|
||||||
|
if (thePost077SupplementFor086HasNotModifiedState) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.provider is ${typeof state
|
||||||
|
.NetworkController.provider}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
import { migrate, version } from './087';
|
import { migrate, version } from './087';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
describe('migration #87', () => {
|
describe('migration #87', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should update the version metadata', async () => {
|
it('should update the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -53,6 +63,65 @@ describe('migration #87', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return state unaltered if TokensController state is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
TokensController: false,
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 86,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
const newStorage = await migrate(oldStorage);
|
||||||
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if TokensController state is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
TokensController: false,
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 86,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokensController is boolean`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not capture an exception if TokensController state is an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
TokensController: {
|
||||||
|
allDetectedTokens: {},
|
||||||
|
allIgnoredTokens: {},
|
||||||
|
allTokens: {},
|
||||||
|
detectedTokens: [],
|
||||||
|
ignoredTokens: [],
|
||||||
|
suggestedAssets: [],
|
||||||
|
tokens: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 86,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should remove the suggested assets state', async () => {
|
it('should remove the suggested assets state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
|
@ -24,6 +24,11 @@ export async function migrate(originalVersionedData: {
|
|||||||
|
|
||||||
function transformState(state: Record<string, unknown>) {
|
function transformState(state: Record<string, unknown>) {
|
||||||
if (!isObject(state.TokensController)) {
|
if (!isObject(state.TokensController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.TokensController is ${typeof state.TokensController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
import { migrate } from './088';
|
import { migrate } from './088';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
describe('migration #88', () => {
|
describe('migration #88', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('updates the version metadata', async () => {
|
it('updates the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: { version: 87 },
|
meta: { version: 87 },
|
||||||
@ -26,6 +36,24 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the NftController property is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {},
|
||||||
|
TokensController: {},
|
||||||
|
NftController: false,
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NftController is boolean`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns the state unaltered if the NftController object has no allNftContracts property', async () => {
|
it('returns the state unaltered if the NftController object has no allNftContracts property', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
NftController: {
|
NftController: {
|
||||||
@ -58,6 +86,26 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if it NftController.allNftContracts is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {},
|
||||||
|
TokensController: {},
|
||||||
|
NftController: {
|
||||||
|
allNftContracts: 'foo',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NftController.allNftContracts is string`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns the state unaltered if any value of the NftController.allNftContracts object is not an object itself', async () => {
|
it('returns the state unaltered if any value of the NftController.allNftContracts object is not an object itself', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
NftController: {
|
NftController: {
|
||||||
@ -324,6 +372,26 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if it NftController.allNfts is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {},
|
||||||
|
TokensController: {},
|
||||||
|
NftController: {
|
||||||
|
allNfts: 'foo',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NftController.allNfts is string`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns the state unaltered if any value of the NftController.allNfts object is not an object itself', async () => {
|
it('returns the state unaltered if any value of the NftController.allNfts object is not an object itself', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
NftController: {
|
NftController: {
|
||||||
@ -656,6 +724,91 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if it has no TokenListController property', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokensController: {},
|
||||||
|
NftController: {
|
||||||
|
allNfts: {
|
||||||
|
'0x111': {
|
||||||
|
'0x10': [
|
||||||
|
{
|
||||||
|
name: 'NFT 1',
|
||||||
|
description: 'Description for NFT 1',
|
||||||
|
image: 'nft1.jpg',
|
||||||
|
standard: 'ERC721',
|
||||||
|
tokenId: '1',
|
||||||
|
address: '0xaaa',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
allNftContracts: {
|
||||||
|
'0x111': {
|
||||||
|
'0x10': [
|
||||||
|
{
|
||||||
|
name: 'Contract 1',
|
||||||
|
address: '0xaaa',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokenListController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the TokenListController property is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokensController: {},
|
||||||
|
NftController: {
|
||||||
|
allNfts: {
|
||||||
|
'0x111': {
|
||||||
|
'0x10': [
|
||||||
|
{
|
||||||
|
name: 'NFT 1',
|
||||||
|
description: 'Description for NFT 1',
|
||||||
|
image: 'nft1.jpg',
|
||||||
|
standard: 'ERC721',
|
||||||
|
tokenId: '1',
|
||||||
|
address: '0xaaa',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
allNftContracts: {
|
||||||
|
'0x111': {
|
||||||
|
'0x10': [
|
||||||
|
{
|
||||||
|
name: 'Contract 1',
|
||||||
|
address: '0xaaa',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TokenListController: false,
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokenListController is boolean`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns the state unaltered if the TokenListController object has no tokensChainsCache property', async () => {
|
it('returns the state unaltered if the TokenListController object has no tokensChainsCache property', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
TokenListController: {
|
TokenListController: {
|
||||||
@ -688,6 +841,25 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the TokenListController.tokensChainsCache property is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {
|
||||||
|
tokensChainsCache: 'foo',
|
||||||
|
},
|
||||||
|
TokensController: {},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokenListController.tokensChainsCache is string`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('rewrites TokenListController.tokensChainsCache so that decimal chain IDs are converted to hex strings', async () => {
|
it('rewrites TokenListController.tokensChainsCache so that decimal chain IDs are converted to hex strings', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: { version: 87 },
|
meta: { version: 87 },
|
||||||
@ -919,6 +1091,39 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if it has no TokensController property', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokensController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the TokensController property is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {},
|
||||||
|
TokensController: false,
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokensController is boolean`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns the state unaltered if the TokensController object has no allTokens property', async () => {
|
it('returns the state unaltered if the TokensController object has no allTokens property', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
TokensController: {
|
TokensController: {
|
||||||
@ -951,6 +1156,25 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the TokensController.allTokens property is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {},
|
||||||
|
TokensController: {
|
||||||
|
allTokens: 'foo',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokensController.allTokens is string`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('rewrites TokensController.allTokens so that decimal chain IDs are converted to hex strings', async () => {
|
it('rewrites TokensController.allTokens so that decimal chain IDs are converted to hex strings', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: { version: 87 },
|
meta: { version: 87 },
|
||||||
@ -1163,6 +1387,25 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the TokensController.allIgnoredTokens property is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {},
|
||||||
|
TokensController: {
|
||||||
|
allIgnoredTokens: 'foo',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokensController.allIgnoredTokens is string`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('rewrites TokensController.allIgnoredTokens so that decimal chain IDs are converted to hex strings', async () => {
|
it('rewrites TokensController.allIgnoredTokens so that decimal chain IDs are converted to hex strings', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: { version: 87 },
|
meta: { version: 87 },
|
||||||
@ -1323,6 +1566,25 @@ describe('migration #88', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the TokensController.allDetectedTokens property is not an object', async () => {
|
||||||
|
const oldData = {
|
||||||
|
TokenListController: {},
|
||||||
|
TokensController: {
|
||||||
|
allDetectedTokens: 'foo',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: { version: 87 },
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.TokensController.allDetectedTokens is string`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('rewrites TokensController.allDetectedTokens so that decimal chain IDs are converted to hex strings', async () => {
|
it('rewrites TokensController.allDetectedTokens so that decimal chain IDs are converted to hex strings', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: { version: 87 },
|
meta: { version: 87 },
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { hasProperty, Hex, isObject, isStrictHexString } from '@metamask/utils';
|
import { hasProperty, Hex, isObject, isStrictHexString } from '@metamask/utils';
|
||||||
import { BN } from 'ethereumjs-util';
|
import { BN } from 'ethereumjs-util';
|
||||||
import { cloneDeep, mapKeys } from 'lodash';
|
import { cloneDeep, mapKeys } from 'lodash';
|
||||||
|
import log from 'loglevel';
|
||||||
|
|
||||||
type VersionedData = {
|
type VersionedData = {
|
||||||
meta: { version: number };
|
meta: { version: number };
|
||||||
@ -70,6 +71,16 @@ function migrateData(state: Record<string, unknown>): void {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (hasProperty(nftControllerState, 'allNftContracts')) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NftController.allNftContracts is ${typeof nftControllerState.allNftContracts}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`typeof state.NftController.allNftContracts is ${typeof nftControllerState.allNftContracts}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate NftController.allNfts
|
// Migrate NftController.allNfts
|
||||||
@ -96,9 +107,25 @@ function migrateData(state: Record<string, unknown>): void {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (hasProperty(nftControllerState, 'allNfts')) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NftController.allNfts is ${typeof nftControllerState.allNfts}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`typeof state.NftController.allNfts is ${typeof nftControllerState.allNfts}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.NftController = nftControllerState;
|
state.NftController = nftControllerState;
|
||||||
|
} else if (hasProperty(state, 'NftController')) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(`typeof state.NftController is ${typeof state.NftController}`),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn(`typeof state.NftController is undefined`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -124,7 +151,24 @@ function migrateData(state: Record<string, unknown>): void {
|
|||||||
tokenListControllerState.tokensChainsCache,
|
tokenListControllerState.tokensChainsCache,
|
||||||
(_, chainId: string) => toHex(chainId),
|
(_, chainId: string) => toHex(chainId),
|
||||||
);
|
);
|
||||||
|
} else if (hasProperty(tokenListControllerState, 'tokensChainsCache')) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.TokenListController.tokensChainsCache is ${typeof state
|
||||||
|
.TokenListController.tokensChainsCache}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`typeof state.TokenListController.tokensChainsCache is undefined`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.TokenListController is ${typeof state.TokenListController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -150,6 +194,16 @@ function migrateData(state: Record<string, unknown>): void {
|
|||||||
allTokens,
|
allTokens,
|
||||||
(_, chainId: string) => toHex(chainId),
|
(_, chainId: string) => toHex(chainId),
|
||||||
);
|
);
|
||||||
|
} else if (hasProperty(tokensControllerState, 'allTokens')) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.TokensController.allTokens is ${typeof tokensControllerState.allTokens}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`typeof state.TokensController.allTokens is ${typeof tokensControllerState.allTokens}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate TokensController.allIgnoredTokens
|
// Migrate TokensController.allIgnoredTokens
|
||||||
@ -169,6 +223,16 @@ function migrateData(state: Record<string, unknown>): void {
|
|||||||
allIgnoredTokens,
|
allIgnoredTokens,
|
||||||
(_, chainId: string) => toHex(chainId),
|
(_, chainId: string) => toHex(chainId),
|
||||||
);
|
);
|
||||||
|
} else if (hasProperty(tokensControllerState, 'allIgnoredTokens')) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.TokensController.allIgnoredTokens is ${typeof tokensControllerState.allIgnoredTokens}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`typeof state.TokensController.allIgnoredTokens is ${typeof tokensControllerState.allIgnoredTokens}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate TokensController.allDetectedTokens
|
// Migrate TokensController.allDetectedTokens
|
||||||
@ -188,9 +252,25 @@ function migrateData(state: Record<string, unknown>): void {
|
|||||||
allDetectedTokens,
|
allDetectedTokens,
|
||||||
(_, chainId: string) => toHex(chainId),
|
(_, chainId: string) => toHex(chainId),
|
||||||
);
|
);
|
||||||
|
} else if (hasProperty(tokensControllerState, 'allDetectedTokens')) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.TokensController.allDetectedTokens is ${typeof tokensControllerState.allDetectedTokens}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`typeof state.TokensController.allDetectedTokens is ${typeof tokensControllerState.allDetectedTokens}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.TokensController = tokensControllerState;
|
state.TokensController = tokensControllerState;
|
||||||
|
} else {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.TokensController is ${typeof state.TokensController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
import { migrate, version } from './089';
|
import { migrate, version } from './089';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
startSession: jest.fn(),
|
||||||
|
endSession: jest.fn(),
|
||||||
|
toggleSession: jest.fn(),
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('uuid', () => {
|
jest.mock('uuid', () => {
|
||||||
const actual = jest.requireActual('uuid');
|
const actual = jest.requireActual('uuid');
|
||||||
|
|
||||||
@ -10,6 +19,10 @@ jest.mock('uuid', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('migration #89', () => {
|
describe('migration #89', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should update the version metadata', async () => {
|
it('should update the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -39,6 +52,25 @@ describe('migration #89', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 88,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is no network controller providerConfig state', async () => {
|
it('should return state unaltered if there is no network controller providerConfig state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
@ -61,6 +93,32 @@ describe('migration #89', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller providerConfig state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
networkConfigurations: {
|
||||||
|
id1: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 88,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController.providerConfig is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if the providerConfig already has an id', async () => {
|
it('should return state unaltered if the providerConfig already has an id', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
|
@ -66,6 +66,19 @@ function transformState(state: Record<string, unknown>) {
|
|||||||
...state,
|
...state,
|
||||||
NetworkController: state.NetworkController,
|
NetworkController: state.NetworkController,
|
||||||
};
|
};
|
||||||
|
} else if (!isObject(state.NetworkController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (!isObject(state.NetworkController.providerConfig)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.providerConfig is ${typeof state
|
||||||
|
.NetworkController.providerConfig}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,17 @@ import { migrate, version } from './090';
|
|||||||
|
|
||||||
const PREVIOUS_VERSION = version - 1;
|
const PREVIOUS_VERSION = version - 1;
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
describe('migration #90', () => {
|
describe('migration #90', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('updates the version metadata', async () => {
|
it('updates the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -31,7 +41,7 @@ describe('migration #90', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not change the state if the phishing controller state is invalid', async () => {
|
it('captures an exception if the phishing controller state is invalid', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
version: PREVIOUS_VERSION,
|
version: PREVIOUS_VERSION,
|
||||||
@ -39,9 +49,12 @@ describe('migration #90', () => {
|
|||||||
data: { PhishingController: 'this is not valid' },
|
data: { PhishingController: 'this is not valid' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const newStorage = await migrate(oldStorage);
|
await migrate(oldStorage);
|
||||||
|
|
||||||
expect(newStorage.data).toStrictEqual(oldStorage.data);
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.PhishingController is string`),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not change the state if the listState property does not exist', async () => {
|
it('does not change the state if the listState property does not exist', async () => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { hasProperty, isObject } from '@metamask/utils';
|
import { hasProperty, isObject } from '@metamask/utils';
|
||||||
|
import log from 'loglevel';
|
||||||
|
|
||||||
export const version = 90;
|
export const version = 90;
|
||||||
|
|
||||||
@ -23,11 +24,22 @@ export async function migrate(originalVersionedData: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transformState(state: Record<string, unknown>) {
|
function transformState(state: Record<string, unknown>) {
|
||||||
if (
|
if (!hasProperty(state, 'PhishingController')) {
|
||||||
!hasProperty(state, 'PhishingController') ||
|
log.warn(`typeof state.PhishingController is undefined`);
|
||||||
!isObject(state.PhishingController) ||
|
return state;
|
||||||
!hasProperty(state.PhishingController, 'listState')
|
}
|
||||||
) {
|
if (!isObject(state.PhishingController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.PhishingController is ${typeof state.PhishingController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
if (!hasProperty(state.PhishingController, 'listState')) {
|
||||||
|
log.warn(
|
||||||
|
`typeof state.PhishingController.listState is ${typeof state.PhishingController}`,
|
||||||
|
);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { migrate, version } from './091';
|
import { migrate, version } from './091';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
startSession: jest.fn(),
|
||||||
|
endSession: jest.fn(),
|
||||||
|
toggleSession: jest.fn(),
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('uuid', () => {
|
jest.mock('uuid', () => {
|
||||||
const actual = jest.requireActual('uuid');
|
const actual = jest.requireActual('uuid');
|
||||||
|
|
||||||
@ -11,6 +20,10 @@ jest.mock('uuid', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('migration #91', () => {
|
describe('migration #91', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should update the version metadata', async () => {
|
it('should update the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -40,6 +53,25 @@ describe('migration #91', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 90,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is no network controller networkConfigurations state', async () => {
|
it('should return state unaltered if there is no network controller networkConfigurations state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
@ -60,6 +92,32 @@ describe('migration #91', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller networkConfigurations state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
providerConfig: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 90,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.networkConfigurations is undefined`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if the networkConfigurations all have a chainId', async () => {
|
it('should return state unaltered if the networkConfigurations all have a chainId', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
|
@ -50,6 +50,19 @@ function transformState(state: Record<string, unknown>) {
|
|||||||
...state,
|
...state,
|
||||||
NetworkController: state.NetworkController,
|
NetworkController: state.NetworkController,
|
||||||
};
|
};
|
||||||
|
} else if (!isObject(state.NetworkController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (!isObject(state.NetworkController.networkConfigurations)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.networkConfigurations is ${typeof state
|
||||||
|
.NetworkController.networkConfigurations}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,20 @@ import { migrate, version } from './092';
|
|||||||
|
|
||||||
const PREVIOUS_VERSION = version - 1;
|
const PREVIOUS_VERSION = version - 1;
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
startSession: jest.fn(),
|
||||||
|
endSession: jest.fn(),
|
||||||
|
toggleSession: jest.fn(),
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
describe('migration #92', () => {
|
describe('migration #92', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should update the version metadata', async () => {
|
it('should update the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -33,6 +46,22 @@ describe('migration #92', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures an exception if the phishing controller state is invalid', async () => {
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: PREVIOUS_VERSION,
|
||||||
|
},
|
||||||
|
data: { PhishingController: 'this is not valid' },
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.PhishingController is string`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is no phishing controller last fetched state', async () => {
|
it('should return state unaltered if there is no phishing controller last fetched state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { hasProperty, isObject } from '@metamask/utils';
|
import { hasProperty, isObject } from '@metamask/utils';
|
||||||
|
import log from 'loglevel';
|
||||||
|
|
||||||
export const version = 92;
|
export const version = 92;
|
||||||
|
|
||||||
@ -30,6 +31,14 @@ function transformState(state: Record<string, unknown>) {
|
|||||||
) {
|
) {
|
||||||
delete state.PhishingController.stalelistLastFetched;
|
delete state.PhishingController.stalelistLastFetched;
|
||||||
delete state.PhishingController.hotlistLastFetched;
|
delete state.PhishingController.hotlistLastFetched;
|
||||||
|
} else if (hasProperty(state, 'PhishingController')) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.PhishingController is ${typeof state.PhishingController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log.warn(`typeof state.PhishingController is undefined`);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,20 @@ import { migrate, version } from './093';
|
|||||||
|
|
||||||
const PREVIOUS_VERSION = version - 1;
|
const PREVIOUS_VERSION = version - 1;
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
startSession: jest.fn(),
|
||||||
|
endSession: jest.fn(),
|
||||||
|
toggleSession: jest.fn(),
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
describe('migration #93', () => {
|
describe('migration #93', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should update the version metadata', async () => {
|
it('should update the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -33,6 +46,25 @@ describe('migration #93', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: PREVIOUS_VERSION,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is no network controller providerConfig state', async () => {
|
it('should return state unaltered if there is no network controller providerConfig state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
@ -55,6 +87,32 @@ describe('migration #93', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller providerConfig state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
networkConfigurations: {
|
||||||
|
id1: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: PREVIOUS_VERSION,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController.providerConfig is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is already a ticker in the providerConfig state', async () => {
|
it('should return state unaltered if there is already a ticker in the providerConfig state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
|
@ -44,6 +44,22 @@ function transformState(state: Record<string, unknown>) {
|
|||||||
...state,
|
...state,
|
||||||
NetworkController: state.NetworkController,
|
NetworkController: state.NetworkController,
|
||||||
};
|
};
|
||||||
|
} else if (!isObject(state.NetworkController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
isObject(state.NetworkController) &&
|
||||||
|
!isObject(state.NetworkController.providerConfig)
|
||||||
|
) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.providerConfig is ${typeof state
|
||||||
|
.NetworkController.providerConfig}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,20 @@ import { NetworkType } from '@metamask/controller-utils';
|
|||||||
import { NetworkStatus } from '@metamask/network-controller';
|
import { NetworkStatus } from '@metamask/network-controller';
|
||||||
import { migrate, version } from './094';
|
import { migrate, version } from './094';
|
||||||
|
|
||||||
|
const sentryCaptureExceptionMock = jest.fn();
|
||||||
|
|
||||||
|
global.sentry = {
|
||||||
|
startSession: jest.fn(),
|
||||||
|
endSession: jest.fn(),
|
||||||
|
toggleSession: jest.fn(),
|
||||||
|
captureException: sentryCaptureExceptionMock,
|
||||||
|
};
|
||||||
|
|
||||||
describe('migration #94', () => {
|
describe('migration #94', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should update the version metadata', async () => {
|
it('should update the version metadata', async () => {
|
||||||
const oldStorage = {
|
const oldStorage = {
|
||||||
meta: {
|
meta: {
|
||||||
@ -32,6 +45,25 @@ describe('migration #94', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 93,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is no network controller providerConfig state', async () => {
|
it('should return state unaltered if there is no network controller providerConfig state', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
@ -54,6 +86,137 @@ describe('migration #94', () => {
|
|||||||
expect(newStorage.data).toStrictEqual(oldData);
|
expect(newStorage.data).toStrictEqual(oldData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no network controller providerConfig state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
networkConfigurations: {
|
||||||
|
id1: {
|
||||||
|
foo: 'bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 93,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(`typeof state.NetworkController.providerConfig is undefined`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should capture an exception if there is no providerConfig.id and no providerConfig.type value in state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
providerConfig: {
|
||||||
|
ticker: 'NET',
|
||||||
|
chainId: '0x189123',
|
||||||
|
nickname: 'A Network',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 93,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.providerConfig.id is undefined and state.NetworkController.providerConfig.type is undefined`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not capture an exception if there is a providerConfig.id in state', async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
providerConfig: {
|
||||||
|
ticker: 'NET',
|
||||||
|
chainId: '0x189123',
|
||||||
|
nickname: 'A Network',
|
||||||
|
id: 'foobar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 93,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should capture an exception if there is no providerConfig.id and the providerConfig.type value is ${NetworkType.rpc} in state`, async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
providerConfig: {
|
||||||
|
ticker: 'NET',
|
||||||
|
chainId: '0x189123',
|
||||||
|
nickname: 'A Network',
|
||||||
|
type: NetworkType.rpc,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 93,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledWith(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.providerConfig.id is undefined and state.NetworkController.providerConfig.type is ${NetworkType.rpc}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should not capture an exception if there is no providerConfig.id and the providerConfig.type value is not ${NetworkType.rpc} in state`, async () => {
|
||||||
|
const oldData = {
|
||||||
|
other: 'data',
|
||||||
|
NetworkController: {
|
||||||
|
providerConfig: {
|
||||||
|
ticker: 'NET',
|
||||||
|
chainId: '0x189123',
|
||||||
|
nickname: 'A Network',
|
||||||
|
type: 'NOT_AN_RPC_TYPE',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oldStorage = {
|
||||||
|
meta: {
|
||||||
|
version: 93,
|
||||||
|
},
|
||||||
|
data: oldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
await migrate(oldStorage);
|
||||||
|
|
||||||
|
expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return state unaltered if there is a providerConfig.id value in state but it is not a string', async () => {
|
it('should return state unaltered if there is a providerConfig.id value in state but it is not a string', async () => {
|
||||||
const oldData = {
|
const oldData = {
|
||||||
other: 'data',
|
other: 'data',
|
||||||
|
@ -84,6 +84,35 @@ function transformState(state: Record<string, unknown>) {
|
|||||||
selectedNetworkClientId,
|
selectedNetworkClientId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
} else if (!isObject(state.NetworkController)) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController is ${typeof state.NetworkController}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
isObject(state.NetworkController) &&
|
||||||
|
!isObject(state.NetworkController.providerConfig)
|
||||||
|
) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.providerConfig is ${typeof state
|
||||||
|
.NetworkController.providerConfig}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
isObject(state.NetworkController) &&
|
||||||
|
isObject(state.NetworkController.providerConfig)
|
||||||
|
) {
|
||||||
|
global.sentry?.captureException?.(
|
||||||
|
new Error(
|
||||||
|
`typeof state.NetworkController.providerConfig.id is ${typeof state
|
||||||
|
.NetworkController.providerConfig
|
||||||
|
.id} and state.NetworkController.providerConfig.type is ${
|
||||||
|
state.NetworkController.providerConfig.type
|
||||||
|
}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
4
types/global.d.ts
vendored
4
types/global.d.ts
vendored
@ -9,7 +9,7 @@ declare class Platform {
|
|||||||
closeCurrentWindow: () => void;
|
closeCurrentWindow: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare class SentryObject extends Sentry {
|
type SentryObject = Sentry & {
|
||||||
// Verifies that the user has opted into metrics and then updates the sentry
|
// Verifies that the user has opted into metrics and then updates the sentry
|
||||||
// instance to track sessions and begins the session.
|
// instance to track sessions and begins the session.
|
||||||
startSession: () => void;
|
startSession: () => void;
|
||||||
@ -20,7 +20,7 @@ declare class SentryObject extends Sentry {
|
|||||||
|
|
||||||
// Calls either startSession or endSession based on optin status
|
// Calls either startSession or endSession based on optin status
|
||||||
toggleSession: () => void;
|
toggleSession: () => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export declare global {
|
export declare global {
|
||||||
var platform: Platform;
|
var platform: Platform;
|
||||||
|
Loading…
Reference in New Issue
Block a user