mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-24 19:10:22 +01:00
MV3: add retry logic to actions (#15337)
This commit is contained in:
parent
ece5901b40
commit
99ed42b3dc
@ -11,7 +11,7 @@ import MetaMetricsProviderStorybook from './metametrics';
|
|||||||
import testData from './test-data.js';
|
import testData from './test-data.js';
|
||||||
import { Router } from 'react-router-dom';
|
import { Router } from 'react-router-dom';
|
||||||
import { createBrowserHistory } from 'history';
|
import { createBrowserHistory } from 'history';
|
||||||
import { _setBackgroundConnection } from '../ui/store/actions';
|
import { _setBackgroundConnection } from '../ui/store/action-queue';
|
||||||
import MetaMaskStorybookTheme from './metamask-storybook-theme';
|
import MetaMaskStorybookTheme from './metamask-storybook-theme';
|
||||||
import addons from '@storybook/addons';
|
import addons from '@storybook/addons';
|
||||||
|
|
||||||
|
@ -71,6 +71,8 @@ async function start() {
|
|||||||
const messageListener = (message) => {
|
const messageListener = (message) => {
|
||||||
if (message?.name === 'CONNECTION_READY') {
|
if (message?.name === 'CONNECTION_READY') {
|
||||||
if (isUIInitialised) {
|
if (isUIInitialised) {
|
||||||
|
// Currently when service worker is revived we create new streams
|
||||||
|
// in later version we might try to improve it by reviving same streams.
|
||||||
updateUiStreams();
|
updateUiStreams();
|
||||||
} else {
|
} else {
|
||||||
initializeUiWithTab(activeTab);
|
initializeUiWithTab(activeTab);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as actions from '../../ui/store/actions';
|
import { _setBackgroundConnection } from '../../ui/store/action-queue';
|
||||||
|
|
||||||
export const setBackgroundConnection = (backgroundConnection = {}) => {
|
export const setBackgroundConnection = (backgroundConnection = {}) => {
|
||||||
actions._setBackgroundConnection(backgroundConnection);
|
_setBackgroundConnection(backgroundConnection);
|
||||||
};
|
};
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
} from './ducks/metamask/metamask';
|
} from './ducks/metamask/metamask';
|
||||||
import Root from './pages';
|
import Root from './pages';
|
||||||
import txHelper from './helpers/utils/tx-helper';
|
import txHelper from './helpers/utils/tx-helper';
|
||||||
|
import { _setBackgroundConnection } from './store/action-queue';
|
||||||
|
|
||||||
log.setLevel(global.METAMASK_DEBUG ? 'debug' : 'warn');
|
log.setLevel(global.METAMASK_DEBUG ? 'debug' : 'warn');
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ let reduxStore;
|
|||||||
* @param backgroundConnection - connection object to background
|
* @param backgroundConnection - connection object to background
|
||||||
*/
|
*/
|
||||||
export const updateBackgroundConnection = (backgroundConnection) => {
|
export const updateBackgroundConnection = (backgroundConnection) => {
|
||||||
actions._setBackgroundConnection(backgroundConnection);
|
_setBackgroundConnection(backgroundConnection);
|
||||||
backgroundConnection.onNotification((data) => {
|
backgroundConnection.onNotification((data) => {
|
||||||
if (data.method === 'sendUpdate') {
|
if (data.method === 'sendUpdate') {
|
||||||
reduxStore.dispatch(actions.updateMetamaskState(data.params[0]));
|
reduxStore.dispatch(actions.updateMetamaskState(data.params[0]));
|
||||||
|
200
ui/store/action-queue/index.js
Normal file
200
ui/store/action-queue/index.js
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
import pify from 'pify';
|
||||||
|
import { isManifestV3 } from '../../../shared/modules/mv3.utils';
|
||||||
|
|
||||||
|
// // A simplified pify maybe?
|
||||||
|
// function pify(apiObject) {
|
||||||
|
// return Object.keys(apiObject).reduce((promisifiedAPI, key) => {
|
||||||
|
// if (apiObject[key].apply) { // depending on our browser support we might use a nicer check for functions here
|
||||||
|
// promisifiedAPI[key] = function (...args) {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// return apiObject[key](
|
||||||
|
// ...args,
|
||||||
|
// (err, result) => {
|
||||||
|
// if (err) {
|
||||||
|
// reject(err);
|
||||||
|
// } else {
|
||||||
|
// resolve(result);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// return promisifiedAPI;
|
||||||
|
// }, {});
|
||||||
|
// }
|
||||||
|
|
||||||
|
let background = null;
|
||||||
|
let promisifiedBackground = null;
|
||||||
|
|
||||||
|
const actionRetryQueue = [];
|
||||||
|
|
||||||
|
function failQueue() {
|
||||||
|
actionRetryQueue.forEach(({ reject }) =>
|
||||||
|
reject(
|
||||||
|
Error('Background operation cancelled while waiting for connection.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops the entire actions queue. Rejects all actions in the queue unless silently==true
|
||||||
|
* Does not affect the single action that is currently being processed.
|
||||||
|
*
|
||||||
|
* @param {boolean} [silently]
|
||||||
|
*/
|
||||||
|
export function dropQueue(silently) {
|
||||||
|
if (!silently) {
|
||||||
|
failQueue();
|
||||||
|
}
|
||||||
|
actionRetryQueue.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add action to queue
|
||||||
|
const executeActionOrAddToRetryQueue = (item) => {
|
||||||
|
if (actionRetryQueue.some((act) => act.actionId === item.actionId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (background.connectionStream.readable) {
|
||||||
|
executeAction({
|
||||||
|
action: item,
|
||||||
|
disconnectSideeffect: () => actionRetryQueue.push(item),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
actionRetryQueue.push(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise-style call to background method
|
||||||
|
* In MV2: invokes promisifiedBackground method directly.
|
||||||
|
* In MV3: action is added to retry queue, along with resolve handler to be executed on completion,
|
||||||
|
* the queue is then immediately processed if background connection is available.
|
||||||
|
* On completion (successful or error) the action is removed from the retry queue.
|
||||||
|
*
|
||||||
|
* @param {string} method - name of the background method
|
||||||
|
* @param {Array} [args] - arguments to that method, if any
|
||||||
|
* @param {any} [actionId] - if an action with the === same id is submitted, it'll be ignored if already in queue waiting for a retry.
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function submitRequestToBackground(
|
||||||
|
method,
|
||||||
|
args = [],
|
||||||
|
actionId = Date.now() + Math.random(), // current date is not guaranteed to be unique
|
||||||
|
) {
|
||||||
|
if (isManifestV3) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
executeActionOrAddToRetryQueue({
|
||||||
|
actionId,
|
||||||
|
request: { method, args },
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return promisifiedBackground[method](...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback-style call to background method
|
||||||
|
* In MV2: invokes promisifiedBackground method directly.
|
||||||
|
* In MV3: action is added to retry queue, along with resolve handler to be executed on completion,
|
||||||
|
* the queue is then immediately processed if background connection is available.
|
||||||
|
* On completion (successful or error) the action is removed from the retry queue.
|
||||||
|
*
|
||||||
|
* @param {string} method - name of the background method
|
||||||
|
* @param {Array} [args] - arguments to that method, if any
|
||||||
|
* @param callback - Node style (error, result) callback for finishing the operation
|
||||||
|
* @param {any} [actionId] - if an action with the === same id is submitted, it'll be ignored if already in queue.
|
||||||
|
*/
|
||||||
|
export const callBackgroundMethod = (
|
||||||
|
method,
|
||||||
|
args = [],
|
||||||
|
callback,
|
||||||
|
actionId = Date.now() + Math.random(), // current date is not guaranteed to be unique
|
||||||
|
) => {
|
||||||
|
if (isManifestV3) {
|
||||||
|
const resolve = (value) => callback(null, value);
|
||||||
|
const reject = (err) => callback(err);
|
||||||
|
executeActionOrAddToRetryQueue({
|
||||||
|
actionId,
|
||||||
|
request: { method, args },
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
background[method](...args, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function executeAction({ action, disconnectSideeffect }) {
|
||||||
|
const {
|
||||||
|
request: { method, args },
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
} = action;
|
||||||
|
try {
|
||||||
|
resolve(await promisifiedBackground[method](...args));
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
background.DisconnectError && // necessary to not break compatibility with background stubs or non-default implementations
|
||||||
|
err instanceof background.DisconnectError
|
||||||
|
) {
|
||||||
|
disconnectSideeffect(action);
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let processingQueue = false;
|
||||||
|
|
||||||
|
// Clears list of pending action in actionRetryQueue
|
||||||
|
// The results of background calls are wired up to the original promises that's been returned
|
||||||
|
// The first method on the queue gets called synchronously to make testing and reasoning about
|
||||||
|
// a single request to an open connection easier.
|
||||||
|
async function processActionRetryQueue() {
|
||||||
|
if (processingQueue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processingQueue = true;
|
||||||
|
try {
|
||||||
|
while (
|
||||||
|
background.connectionStream.readable &&
|
||||||
|
actionRetryQueue.length > 0
|
||||||
|
) {
|
||||||
|
// If background disconnects and fails the action, the next one will not be taken off the queue.
|
||||||
|
// Retrying an action that failed because of connection loss while it was processing is not supported.
|
||||||
|
const item = actionRetryQueue.shift();
|
||||||
|
await executeAction({
|
||||||
|
action: item,
|
||||||
|
disconnectSideeffect: () => actionRetryQueue.unshift(item),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// error in the queue mechanism itself, the action was malformed
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
processingQueue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets/replaces the background connection reference
|
||||||
|
* Under MV3 it also triggers queue processing if the new background is connected
|
||||||
|
*
|
||||||
|
* @param {*} backgroundConnection
|
||||||
|
*/
|
||||||
|
export async function _setBackgroundConnection(backgroundConnection) {
|
||||||
|
background = backgroundConnection;
|
||||||
|
promisifiedBackground = pify(background);
|
||||||
|
if (isManifestV3) {
|
||||||
|
if (processingQueue) {
|
||||||
|
console.warn(
|
||||||
|
'_setBackgroundConnection called while a queue was processing and not disconnected yet',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Process all actions collected while connection stream was not available.
|
||||||
|
processActionRetryQueue();
|
||||||
|
}
|
||||||
|
}
|
358
ui/store/action-queue/index.test.js
Normal file
358
ui/store/action-queue/index.test.js
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import {
|
||||||
|
dropQueue,
|
||||||
|
callBackgroundMethod,
|
||||||
|
submitRequestToBackground,
|
||||||
|
_setBackgroundConnection,
|
||||||
|
} from '.';
|
||||||
|
|
||||||
|
// This file tests only MV3 queue scenario
|
||||||
|
// MV2 tests are already covered by '../actions.test.js'
|
||||||
|
|
||||||
|
jest.mock('../../../shared/modules/mv3.utils', () => {
|
||||||
|
return {
|
||||||
|
isManifestV3: () => true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ActionQueue', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
sinon.restore();
|
||||||
|
dropQueue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dropQueue', () => {
|
||||||
|
it('rejects all pending actions by default', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
backgroundFunction: sinon.stub().yields(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const result = submitRequestToBackground('backgroundFunction');
|
||||||
|
dropQueue();
|
||||||
|
|
||||||
|
await expect(result).rejects.toThrow(
|
||||||
|
'Background operation cancelled while waiting for connection.',
|
||||||
|
);
|
||||||
|
expect(background.backgroundFunction.called).toStrictEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('submitRequestToBackground', () => {
|
||||||
|
it('calls promisified background method if the stream is connected', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: true,
|
||||||
|
},
|
||||||
|
backgroundFunction1: sinon.stub().yields(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
submitRequestToBackground('backgroundFunction1');
|
||||||
|
expect(background.backgroundFunction1.called).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not calls promisified background method if the stream is not connected', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
backgroundFunction2: sinon.stub().yields(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
submitRequestToBackground('backgroundFunction2');
|
||||||
|
expect(background.backgroundFunction2.called).toStrictEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls promisified background method on stream reconnection', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
backgroundFunction3: sinon.stub().yields(),
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const requestPromise = submitRequestToBackground('backgroundFunction3');
|
||||||
|
|
||||||
|
background.connectionStream = {
|
||||||
|
readable: true,
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
await requestPromise;
|
||||||
|
expect(background.backgroundFunction3.calledOnce).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resolves if backgroundFunction resolves', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: true,
|
||||||
|
},
|
||||||
|
backgroundFunction4: (cb) => {
|
||||||
|
return cb(null, 'test');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
await expect(
|
||||||
|
submitRequestToBackground('backgroundFunction4'),
|
||||||
|
).resolves.toStrictEqual('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects if backgroundFunction throws exception', async () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: true,
|
||||||
|
},
|
||||||
|
backgroundFunction: () => {
|
||||||
|
throw Error('test');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
await expect(
|
||||||
|
submitRequestToBackground('backgroundFunction'),
|
||||||
|
).rejects.toThrow('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls methods in parallel when connection available', async () => {
|
||||||
|
const trace = {};
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: true,
|
||||||
|
},
|
||||||
|
first: (cb) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
trace.firstDone = Date.now();
|
||||||
|
cb(null, 'first');
|
||||||
|
}, 5);
|
||||||
|
},
|
||||||
|
second: (cb) => {
|
||||||
|
trace.secondStarted = Date.now();
|
||||||
|
setTimeout(() => cb(null, 'second'), 10);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const scheduled = Promise.all([
|
||||||
|
submitRequestToBackground('first'),
|
||||||
|
submitRequestToBackground('second'),
|
||||||
|
]);
|
||||||
|
await scheduled;
|
||||||
|
expect(trace.firstDone).toBeGreaterThan(trace.secondStarted);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('processes the queue sequentially when connection is restored', async () => {
|
||||||
|
const trace = {};
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
first: (cb) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
trace.firstDone = Date.now();
|
||||||
|
cb(null, 'first');
|
||||||
|
}, 5);
|
||||||
|
},
|
||||||
|
second: (cb) => {
|
||||||
|
trace.secondStarted = Date.now();
|
||||||
|
setTimeout(() => cb(null, 'second'), 10);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const scheduled = Promise.all([
|
||||||
|
submitRequestToBackground('first'),
|
||||||
|
submitRequestToBackground('second'),
|
||||||
|
]);
|
||||||
|
background.connectionStream.readable = true;
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
await scheduled;
|
||||||
|
expect(trace.firstDone).toBeLessThanOrEqual(trace.secondStarted);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ensures actions in queue will not repeat once finished', async () => {
|
||||||
|
const trace = { calls: 0 };
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
first: (cb) => {
|
||||||
|
trace.calls += 1;
|
||||||
|
setTimeout(() => {
|
||||||
|
trace.firstDone = Date.now();
|
||||||
|
cb(null, 'first');
|
||||||
|
}, 5);
|
||||||
|
},
|
||||||
|
second: (cb) => {
|
||||||
|
trace.calls += 1;
|
||||||
|
trace.secondStarted = Date.now();
|
||||||
|
setTimeout(() => cb(null, 'second'), 10);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const scheduled = Promise.all([
|
||||||
|
submitRequestToBackground('first'),
|
||||||
|
submitRequestToBackground('second'),
|
||||||
|
]);
|
||||||
|
background.connectionStream.readable = true;
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
await scheduled;
|
||||||
|
_setBackgroundConnection(background); // once all actions finished, this triggers draining the queue again
|
||||||
|
expect(trace.firstDone).toBeLessThanOrEqual(trace.secondStarted);
|
||||||
|
expect(trace.calls).toStrictEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops processng the queue if connection is lost', async () => {
|
||||||
|
const trace = {};
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
first: (cb) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
trace.firstDone = true;
|
||||||
|
background.connectionStream.readable = false;
|
||||||
|
cb(Error('lost connection'));
|
||||||
|
}, 5);
|
||||||
|
},
|
||||||
|
second: sinon.stub().yields(),
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const scheduled = Promise.race([
|
||||||
|
submitRequestToBackground('first').catch(() => ({})),
|
||||||
|
submitRequestToBackground('second'),
|
||||||
|
]);
|
||||||
|
background.connectionStream.readable = true;
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
await scheduled;
|
||||||
|
await Promise.resolve('one more tick'); // One asynchronous tick to avoid depending on implementation details
|
||||||
|
expect(trace.firstDone).toStrictEqual(true);
|
||||||
|
expect(background.second.called).toStrictEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Failing test for a race condition related to how items are removed from queue
|
||||||
|
it('avoids race conditions', async () => {
|
||||||
|
const trace = { first: 0, second: 0 };
|
||||||
|
const flowControl = {};
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
first: (cb) => {
|
||||||
|
trace.first += 1;
|
||||||
|
setTimeout(() => {
|
||||||
|
flowControl.triggerRaceCondition();
|
||||||
|
cb(null, 'first');
|
||||||
|
}, 5);
|
||||||
|
},
|
||||||
|
second: (cb) => {
|
||||||
|
trace.second += 1;
|
||||||
|
setTimeout(() => cb(null, 'second'), 10);
|
||||||
|
},
|
||||||
|
third: sinon.stub().yields(),
|
||||||
|
};
|
||||||
|
flowControl.triggerRaceCondition = () => {
|
||||||
|
flowControl.waitFor = submitRequestToBackground('third');
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const scheduled = Promise.all([
|
||||||
|
submitRequestToBackground('first'),
|
||||||
|
submitRequestToBackground('second'),
|
||||||
|
]);
|
||||||
|
background.connectionStream.readable = true;
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
await scheduled;
|
||||||
|
await flowControl.waitFor;
|
||||||
|
expect(trace.first).toStrictEqual(1);
|
||||||
|
expect(trace.second).toStrictEqual(1);
|
||||||
|
expect(background.third.calledOnce).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('callBackgroundMethod', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
sinon.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls background method if the stream is connected', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: true,
|
||||||
|
},
|
||||||
|
backgroundFunction: sinon.stub().yields(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
callBackgroundMethod('backgroundFunction', [], () => ({}));
|
||||||
|
expect(background.backgroundFunction.called).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not call background method if the stream is not connected', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
backgroundFunction: sinon.stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
callBackgroundMethod('backgroundFunction', [], () => ({}));
|
||||||
|
expect(background.backgroundFunction.called).toStrictEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls background method on stream reconnection', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: false,
|
||||||
|
},
|
||||||
|
backgroundFunction: sinon.stub().yields(),
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
callBackgroundMethod('backgroundFunction', [], () => ({}));
|
||||||
|
expect(background.backgroundFunction.called).toStrictEqual(false);
|
||||||
|
|
||||||
|
background.connectionStream = {
|
||||||
|
readable: true,
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
expect(background.backgroundFunction.calledOnce).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resolves if backgroundFunction called resolves', async () => {
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: true,
|
||||||
|
},
|
||||||
|
backgroundFunction: (cb) => {
|
||||||
|
return cb(null, 'successViaCallback');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const value = await new Promise((resolve) => {
|
||||||
|
callBackgroundMethod('backgroundFunction', [], (_err, result) => {
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(value).toStrictEqual('successViaCallback');
|
||||||
|
});
|
||||||
|
it('rejects if backgroundFunction called rejects', async () => {
|
||||||
|
const errorViaCallback = Error('errorViaCallback');
|
||||||
|
const background = {
|
||||||
|
connectionStream: {
|
||||||
|
readable: true,
|
||||||
|
},
|
||||||
|
backgroundFunction: (cb) => {
|
||||||
|
return cb(errorViaCallback);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
const value = await new Promise((resolve) => {
|
||||||
|
callBackgroundMethod('backgroundFunction', [], (err) => {
|
||||||
|
resolve(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(value).toStrictEqual(errorViaCallback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
66
ui/store/action-queue/queue.integration.test.js
Normal file
66
ui/store/action-queue/queue.integration.test.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import sinon from 'sinon';
|
||||||
|
import PortStream from 'extension-port-stream';
|
||||||
|
import { setupMultiplex } from '../../../app/scripts/lib/stream-utils';
|
||||||
|
import metaRPCClientFactory from '../../../app/scripts/lib/metaRPCClientFactory';
|
||||||
|
|
||||||
|
import {
|
||||||
|
dropQueue,
|
||||||
|
submitRequestToBackground,
|
||||||
|
_setBackgroundConnection,
|
||||||
|
} from '.';
|
||||||
|
|
||||||
|
jest.mock('../../../shared/modules/mv3.utils', () => {
|
||||||
|
return {
|
||||||
|
isManifestV3: () => true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
describe('queue integration test', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
dropQueue(true);
|
||||||
|
});
|
||||||
|
it('schedules a retry if background method failed because of a disconnect', async () => {
|
||||||
|
let disconnectListener;
|
||||||
|
const extensionPort = {
|
||||||
|
onMessage: {
|
||||||
|
addListener: sinon.stub(),
|
||||||
|
},
|
||||||
|
onDisconnect: {
|
||||||
|
addListener(cb) {
|
||||||
|
disconnectListener = cb;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
postMessage: sinon.stub().callsFake(() => {
|
||||||
|
disconnectListener();
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const connectionStream = new PortStream(extensionPort);
|
||||||
|
const mx = setupMultiplex(connectionStream);
|
||||||
|
const multiplexStream1 = mx.createStream('controller');
|
||||||
|
const background = metaRPCClientFactory(multiplexStream1);
|
||||||
|
|
||||||
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
|
// disconnect will happen on the attempt to send the message
|
||||||
|
const finished = submitRequestToBackground('backgroundFunction').catch(
|
||||||
|
(error) => {
|
||||||
|
// disconnect error should not get propagated, we retry.
|
||||||
|
// eslint-disable-next-line jest/no-conditional-expect
|
||||||
|
expect(error).not.toBeInstanceOf(background.DisconnectError);
|
||||||
|
// eslint-disable-next-line jest/no-conditional-expect
|
||||||
|
expect(error.message).toContain('cancelled');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// We want to make sure we disconnect in the middle of processing, so we have to wait for the control flow to reach postMessage
|
||||||
|
// undetermined number of asynchronous jumps withing the stream implementation leaves no other option
|
||||||
|
await wait(3);
|
||||||
|
// we drop the queue because we're expecting the action to have been returned to the queue and this is the simplest way to check that
|
||||||
|
dropQueue();
|
||||||
|
|
||||||
|
expect(extensionPort.postMessage.calledOnce).toStrictEqual(true);
|
||||||
|
await finished;
|
||||||
|
});
|
||||||
|
});
|
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ import { TRANSACTION_STATUSES } from '../../shared/constants/transaction';
|
|||||||
import { DEVICE_NAMES } from '../../shared/constants/hardware-wallets';
|
import { DEVICE_NAMES } from '../../shared/constants/hardware-wallets';
|
||||||
import { GAS_LIMITS } from '../../shared/constants/gas';
|
import { GAS_LIMITS } from '../../shared/constants/gas';
|
||||||
import * as actions from './actions';
|
import * as actions from './actions';
|
||||||
|
import { _setBackgroundConnection } from './action-queue';
|
||||||
|
|
||||||
const middleware = [thunk];
|
const middleware = [thunk];
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
@ -56,7 +57,7 @@ describe('Actions', () => {
|
|||||||
cb(),
|
cb(),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -81,7 +82,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.submitPassword.callsFake((_, cb) => cb(new Error('error')));
|
background.submitPassword.callsFake((_, cb) => cb(new Error('error')));
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -111,7 +112,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.unMarkPasswordForgotten.callsFake((cb) => cb());
|
background.unMarkPasswordForgotten.callsFake((cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.createNewVaultAndRestore('password', 'test'),
|
actions.createNewVaultAndRestore('password', 'test'),
|
||||||
@ -125,7 +126,7 @@ describe('Actions', () => {
|
|||||||
background.createNewVaultAndRestore.callsFake((_, __, cb) => cb());
|
background.createNewVaultAndRestore.callsFake((_, __, cb) => cb());
|
||||||
background.unMarkPasswordForgotten.callsFake((cb) => cb());
|
background.unMarkPasswordForgotten.callsFake((cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -152,7 +153,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error')),
|
cb(new Error('error')),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -183,7 +184,7 @@ describe('Actions', () => {
|
|||||||
cb(null, Array.from(Buffer.from('test').values())),
|
cb(null, Array.from(Buffer.from('test').values())),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.requestRevealSeedWords());
|
await store.dispatch(actions.requestRevealSeedWords());
|
||||||
expect(verifyPassword.callCount).toStrictEqual(1);
|
expect(verifyPassword.callCount).toStrictEqual(1);
|
||||||
@ -198,7 +199,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error'));
|
cb(new Error('error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -244,7 +245,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
const removeAccount = background.removeAccount.callsFake((_, cb) => cb());
|
const removeAccount = background.removeAccount.callsFake((_, cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
'SHOW_LOADING_INDICATION',
|
'SHOW_LOADING_INDICATION',
|
||||||
@ -271,7 +272,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error'));
|
cb(new Error('error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -299,7 +300,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
const resetAccount = background.resetAccount.callsFake((cb) => cb());
|
const resetAccount = background.resetAccount.callsFake((cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -319,7 +320,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error'));
|
cb(new Error('error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -348,7 +349,7 @@ describe('Actions', () => {
|
|||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.importNewAccount('Private Key', [
|
actions.importNewAccount('Private Key', [
|
||||||
@ -365,7 +366,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error')),
|
cb(new Error('error')),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{
|
{
|
||||||
@ -396,7 +397,7 @@ describe('Actions', () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.addNewAccount(1));
|
await store.dispatch(actions.addNewAccount(1));
|
||||||
expect(addNewAccount.callCount).toStrictEqual(1);
|
expect(addNewAccount.callCount).toStrictEqual(1);
|
||||||
@ -409,7 +410,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error'));
|
cb(new Error('error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -439,7 +440,7 @@ describe('Actions', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.checkHardwareStatus(DEVICE_NAMES.LEDGER, `m/44'/60'/0'/0`),
|
actions.checkHardwareStatus(DEVICE_NAMES.LEDGER, `m/44'/60'/0'/0`),
|
||||||
@ -454,7 +455,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error')),
|
cb(new Error('error')),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -480,7 +481,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
const forgetDevice = background.forgetDevice.callsFake((_, cb) => cb());
|
const forgetDevice = background.forgetDevice.callsFake((_, cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.forgetDevice(DEVICE_NAMES.LEDGER));
|
await store.dispatch(actions.forgetDevice(DEVICE_NAMES.LEDGER));
|
||||||
expect(forgetDevice.callCount).toStrictEqual(1);
|
expect(forgetDevice.callCount).toStrictEqual(1);
|
||||||
@ -491,7 +492,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.forgetDevice.callsFake((_, cb) => cb(new Error('error')));
|
background.forgetDevice.callsFake((_, cb) => cb(new Error('error')));
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -521,7 +522,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.establishLedgerTransportPreference.callsFake((cb) => cb());
|
background.establishLedgerTransportPreference.callsFake((cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.connectHardware(DEVICE_NAMES.LEDGER, 0, `m/44'/60'/0'/0`),
|
actions.connectHardware(DEVICE_NAMES.LEDGER, 0, `m/44'/60'/0'/0`),
|
||||||
@ -538,7 +539,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.establishLedgerTransportPreference.callsFake((cb) => cb());
|
background.establishLedgerTransportPreference.callsFake((cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{
|
{
|
||||||
@ -569,7 +570,7 @@ describe('Actions', () => {
|
|||||||
(_, __, ___, ____, cb) => cb(),
|
(_, __, ___, ____, cb) => cb(),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.unlockHardwareWalletAccounts(
|
actions.unlockHardwareWalletAccounts(
|
||||||
@ -589,7 +590,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error')),
|
cb(new Error('error')),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -613,7 +614,7 @@ describe('Actions', () => {
|
|||||||
it('calls setCurrentCurrency', async () => {
|
it('calls setCurrentCurrency', async () => {
|
||||||
const store = mockStore();
|
const store = mockStore();
|
||||||
background.setCurrentCurrency = sinon.stub().callsFake((_, cb) => cb());
|
background.setCurrentCurrency = sinon.stub().callsFake((_, cb) => cb());
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.setCurrentCurrency('jpy'));
|
await store.dispatch(actions.setCurrentCurrency('jpy'));
|
||||||
expect(background.setCurrentCurrency.callCount).toStrictEqual(1);
|
expect(background.setCurrentCurrency.callCount).toStrictEqual(1);
|
||||||
@ -624,7 +625,7 @@ describe('Actions', () => {
|
|||||||
background.setCurrentCurrency = sinon
|
background.setCurrentCurrency = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsFake((_, cb) => cb(new Error('error')));
|
.callsFake((_, cb) => cb(new Error('error')));
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -655,7 +656,7 @@ describe('Actions', () => {
|
|||||||
cb(null, defaultState.metamask),
|
cb(null, defaultState.metamask),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.signMsg(msgParams));
|
await store.dispatch(actions.signMsg(msgParams));
|
||||||
expect(signMessage.callCount).toStrictEqual(1);
|
expect(signMessage.callCount).toStrictEqual(1);
|
||||||
@ -666,7 +667,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.signMessage.callsFake((_, cb) => cb(new Error('error')));
|
background.signMessage.callsFake((_, cb) => cb(new Error('error')));
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -699,7 +700,7 @@ describe('Actions', () => {
|
|||||||
(_, cb) => cb(null, defaultState.metamask),
|
(_, cb) => cb(null, defaultState.metamask),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.signPersonalMsg(msgParams));
|
await store.dispatch(actions.signPersonalMsg(msgParams));
|
||||||
expect(signPersonalMessage.callCount).toStrictEqual(1);
|
expect(signPersonalMessage.callCount).toStrictEqual(1);
|
||||||
@ -712,7 +713,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error'));
|
cb(new Error('error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -780,7 +781,7 @@ describe('Actions', () => {
|
|||||||
cb(null, defaultState.metamask),
|
cb(null, defaultState.metamask),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.signTypedMsg(msgParamsV3));
|
await store.dispatch(actions.signTypedMsg(msgParamsV3));
|
||||||
expect(signTypedMsg.callCount).toStrictEqual(1);
|
expect(signTypedMsg.callCount).toStrictEqual(1);
|
||||||
@ -791,7 +792,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.signTypedMessage.callsFake((_, cb) => cb(new Error('error')));
|
background.signTypedMessage.callsFake((_, cb) => cb(new Error('error')));
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -837,7 +838,7 @@ describe('Actions', () => {
|
|||||||
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(actions.updateTransaction(txData));
|
await store.dispatch(actions.updateTransaction(txData));
|
||||||
|
|
||||||
@ -865,7 +866,7 @@ describe('Actions', () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -903,7 +904,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
const backgroundSetLocked = background.setLocked.callsFake((cb) => cb());
|
const backgroundSetLocked = background.setLocked.callsFake((cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.lockMetamask());
|
await store.dispatch(actions.lockMetamask());
|
||||||
expect(backgroundSetLocked.callCount).toStrictEqual(1);
|
expect(backgroundSetLocked.callCount).toStrictEqual(1);
|
||||||
@ -916,7 +917,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error'));
|
cb(new Error('error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -945,7 +946,7 @@ describe('Actions', () => {
|
|||||||
setSelectedAddress: setSelectedAddressSpy,
|
setSelectedAddress: setSelectedAddressSpy,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.setSelectedAddress(
|
actions.setSelectedAddress(
|
||||||
@ -966,7 +967,7 @@ describe('Actions', () => {
|
|||||||
setSelectedAddress: setSelectedAddressSpy,
|
setSelectedAddress: setSelectedAddressSpy,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -996,7 +997,7 @@ describe('Actions', () => {
|
|||||||
setSelectedAddress: setSelectedAddressSpy,
|
setSelectedAddress: setSelectedAddressSpy,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(actions.showAccountDetail());
|
await store.dispatch(actions.showAccountDetail());
|
||||||
expect(setSelectedAddressSpy.callCount).toStrictEqual(1);
|
expect(setSelectedAddressSpy.callCount).toStrictEqual(1);
|
||||||
@ -1016,7 +1017,7 @@ describe('Actions', () => {
|
|||||||
setSelectedAddress: setSelectedAddressSpy,
|
setSelectedAddress: setSelectedAddressSpy,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1046,7 +1047,7 @@ describe('Actions', () => {
|
|||||||
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.addToken({
|
actions.addToken({
|
||||||
@ -1076,7 +1077,7 @@ describe('Actions', () => {
|
|||||||
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1114,7 +1115,7 @@ describe('Actions', () => {
|
|||||||
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.ignoreTokens({ tokensToIgnore: '0x0000001' }),
|
actions.ignoreTokens({ tokensToIgnore: '0x0000001' }),
|
||||||
@ -1130,7 +1131,7 @@ describe('Actions', () => {
|
|||||||
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
getState: sinon.stub().callsFake((cb) => cb(null, baseMockState)),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1164,7 +1165,7 @@ describe('Actions', () => {
|
|||||||
setProviderType: setProviderTypeStub,
|
setProviderType: setProviderTypeStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(actions.setProviderType());
|
await store.dispatch(actions.setProviderType());
|
||||||
expect(setProviderTypeStub.callCount).toStrictEqual(1);
|
expect(setProviderTypeStub.callCount).toStrictEqual(1);
|
||||||
@ -1179,7 +1180,7 @@ describe('Actions', () => {
|
|||||||
.callsFake((_, cb) => cb(new Error('error'))),
|
.callsFake((_, cb) => cb(new Error('error'))),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
|
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
|
||||||
@ -1200,7 +1201,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.setCustomRpc.callsFake((_, __, ___, ____, cb) => cb());
|
background.setCustomRpc.callsFake((_, __, ___, ____, cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.setRpcTarget('http://localhost:8545'));
|
await store.dispatch(actions.setRpcTarget('http://localhost:8545'));
|
||||||
expect(background.setCustomRpc.callCount).toStrictEqual(1);
|
expect(background.setCustomRpc.callCount).toStrictEqual(1);
|
||||||
@ -1213,7 +1214,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error')),
|
cb(new Error('error')),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
|
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
|
||||||
@ -1236,7 +1237,7 @@ describe('Actions', () => {
|
|||||||
setAddressBook: setAddressBookStub,
|
setAddressBook: setAddressBookStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(actions.addToAddressBook('0x0000'));
|
await store.dispatch(actions.addToAddressBook('0x0000'));
|
||||||
expect(setAddressBookStub.callCount).toStrictEqual(1);
|
expect(setAddressBookStub.callCount).toStrictEqual(1);
|
||||||
@ -1265,7 +1266,7 @@ describe('Actions', () => {
|
|||||||
exportAccount: exportAccountStub,
|
exportAccount: exportAccountStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1295,7 +1296,7 @@ describe('Actions', () => {
|
|||||||
verifyPassword: verifyPasswordStub,
|
verifyPassword: verifyPasswordStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1324,7 +1325,7 @@ describe('Actions', () => {
|
|||||||
exportAccount: exportAccountStub,
|
exportAccount: exportAccountStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1357,7 +1358,7 @@ describe('Actions', () => {
|
|||||||
setAccountLabel: setAccountLabelStub,
|
setAccountLabel: setAccountLabelStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(
|
await store.dispatch(
|
||||||
actions.setAccountLabel(
|
actions.setAccountLabel(
|
||||||
@ -1377,7 +1378,7 @@ describe('Actions', () => {
|
|||||||
.callsFake((_, __, cb) => cb(new Error('error'))),
|
.callsFake((_, __, cb) => cb(new Error('error'))),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1412,7 +1413,7 @@ describe('Actions', () => {
|
|||||||
setFeatureFlag: setFeatureFlagStub,
|
setFeatureFlag: setFeatureFlagStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(actions.setFeatureFlag());
|
await store.dispatch(actions.setFeatureFlag());
|
||||||
expect(setFeatureFlagStub.callCount).toStrictEqual(1);
|
expect(setFeatureFlagStub.callCount).toStrictEqual(1);
|
||||||
@ -1427,7 +1428,7 @@ describe('Actions', () => {
|
|||||||
.callsFake((_, __, cb) => cb(new Error('error'))),
|
.callsFake((_, __, cb) => cb(new Error('error'))),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1456,7 +1457,7 @@ describe('Actions', () => {
|
|||||||
completeOnboarding: completeOnboardingStub,
|
completeOnboarding: completeOnboardingStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(actions.setCompletedOnboarding());
|
await store.dispatch(actions.setCompletedOnboarding());
|
||||||
expect(completeOnboardingStub.callCount).toStrictEqual(1);
|
expect(completeOnboardingStub.callCount).toStrictEqual(1);
|
||||||
@ -1471,7 +1472,7 @@ describe('Actions', () => {
|
|||||||
.callsFake((cb) => cb(new Error('error'))),
|
.callsFake((cb) => cb(new Error('error'))),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1495,7 +1496,7 @@ describe('Actions', () => {
|
|||||||
it('calls setUseBlockie in background', async () => {
|
it('calls setUseBlockie in background', async () => {
|
||||||
const store = mockStore();
|
const store = mockStore();
|
||||||
const setUseBlockieStub = sinon.stub().callsFake((_, cb) => cb());
|
const setUseBlockieStub = sinon.stub().callsFake((_, cb) => cb());
|
||||||
actions._setBackgroundConnection({ setUseBlockie: setUseBlockieStub });
|
_setBackgroundConnection({ setUseBlockie: setUseBlockieStub });
|
||||||
|
|
||||||
await store.dispatch(actions.setUseBlockie());
|
await store.dispatch(actions.setUseBlockie());
|
||||||
expect(setUseBlockieStub.callCount).toStrictEqual(1);
|
expect(setUseBlockieStub.callCount).toStrictEqual(1);
|
||||||
@ -1507,7 +1508,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error'));
|
cb(new Error('error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection({ setUseBlockie: setUseBlockieStub });
|
_setBackgroundConnection({ setUseBlockie: setUseBlockieStub });
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
|
||||||
@ -1535,7 +1536,7 @@ describe('Actions', () => {
|
|||||||
it('calls expected actions', async () => {
|
it('calls expected actions', async () => {
|
||||||
const store = mockStore();
|
const store = mockStore();
|
||||||
const setCurrentLocaleStub = sinon.stub().callsFake((_, cb) => cb());
|
const setCurrentLocaleStub = sinon.stub().callsFake((_, cb) => cb());
|
||||||
actions._setBackgroundConnection({
|
_setBackgroundConnection({
|
||||||
setCurrentLocale: setCurrentLocaleStub,
|
setCurrentLocale: setCurrentLocaleStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1558,7 +1559,7 @@ describe('Actions', () => {
|
|||||||
const setCurrentLocaleStub = sinon
|
const setCurrentLocaleStub = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsFake((_, cb) => cb(new Error('error')));
|
.callsFake((_, cb) => cb(new Error('error')));
|
||||||
actions._setBackgroundConnection({
|
_setBackgroundConnection({
|
||||||
setCurrentLocale: setCurrentLocaleStub,
|
setCurrentLocale: setCurrentLocaleStub,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1584,7 +1585,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.markPasswordForgotten.callsFake((cb) => cb());
|
background.markPasswordForgotten.callsFake((cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
await store.dispatch(actions.markPasswordForgotten());
|
await store.dispatch(actions.markPasswordForgotten());
|
||||||
|
|
||||||
@ -1603,7 +1604,7 @@ describe('Actions', () => {
|
|||||||
cb(new Error('error')),
|
cb(new Error('error')),
|
||||||
);
|
);
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: 'HIDE_LOADING_INDICATION' },
|
{ type: 'HIDE_LOADING_INDICATION' },
|
||||||
@ -1628,7 +1629,7 @@ describe('Actions', () => {
|
|||||||
|
|
||||||
background.unMarkPasswordForgotten.callsFake((cb) => cb());
|
background.unMarkPasswordForgotten.callsFake((cb) => cb());
|
||||||
|
|
||||||
actions._setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
|
|
||||||
store.dispatch(actions.unMarkPasswordForgotten());
|
store.dispatch(actions.unMarkPasswordForgotten());
|
||||||
|
|
||||||
@ -1687,7 +1688,7 @@ describe('Actions', () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
const txId = 1457634084250832;
|
const txId = 1457634084250832;
|
||||||
|
|
||||||
@ -1742,7 +1743,7 @@ describe('Actions', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
actions._setBackgroundConnection(background.getApi());
|
_setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
await store.dispatch(actions.cancelMsgs(msgsList));
|
await store.dispatch(actions.cancelMsgs(msgsList));
|
||||||
const resultantActions = store.getActions();
|
const resultantActions = store.getActions();
|
||||||
|
Loading…
Reference in New Issue
Block a user