Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more unit tests for SmartTransactionsController #23

Merged
merged 6 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports = {
coverageReporters: ['text', 'html'],
coverageThreshold: {
global: {
branches: 81,
branches: 79,
functions: 91,
lines: 93,
statements: 93,
Expand Down
133 changes: 126 additions & 7 deletions src/SmartTransactionsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import SmartTransactionsController, {
DEFAULT_INTERVAL,
} from './SmartTransactionsController';
import { API_BASE_URL, CHAIN_IDS, CHAIN_IDS_HEX_TO_DEC } from './constants';
import { SmartTransaction } from './types';
import { SmartTransaction, SmartTransactionStatuses } from './types';

const confirmExternalMock = jest.fn();

Expand All @@ -31,9 +31,11 @@ jest.mock('ethers', () => ({
},
}));

const addressFrom = '0x268392a24B6b093127E8581eAfbD1DA228bAdAe3';

const createUnsignedTransaction = () => {
return {
from: '0x268392a24B6b093127E8581eAfbD1DA228bAdAe3',
from: addressFrom,
to: '0x0000000000000000000000000000000000000000',
value: 0,
data: '0x',
Expand Down Expand Up @@ -195,6 +197,8 @@ const testHistory = [

const ethereumChainIdDec = CHAIN_IDS_HEX_TO_DEC[CHAIN_IDS.ETHEREUM];

const trackMetaMetricsEventSpy = jest.fn();

describe('SmartTransactionsController', () => {
let smartTransactionsController: SmartTransactionsController;
let networkListener: (networkState: NetworkState) => void;
Expand All @@ -217,12 +221,8 @@ describe('SmartTransactionsController', () => {
txController: {
confirmExternalTransaction: confirmExternalMock,
},
trackMetaMetricsEvent: jest.fn(),
trackMetaMetricsEvent: trackMetaMetricsEventSpy,
});

jest
.spyOn(smartTransactionsController, 'checkPoll')
.mockImplementation(() => ({}));
});

afterEach(async () => {
Expand Down Expand Up @@ -264,6 +264,30 @@ describe('SmartTransactionsController', () => {
});
});

describe('checkPoll', () => {
it('calls poll if there is no pending transaction and pending transactions', () => {
const pollSpy = jest.spyOn(smartTransactionsController, 'poll');
const { smartTransactionsState } = smartTransactionsController.state;
const pendingStx = createStateAfterPending();
smartTransactionsController.update({
smartTransactionsState: {
...smartTransactionsState,
smartTransactions: {
[CHAIN_IDS.ETHEREUM]: pendingStx as SmartTransaction[],
},
},
});
expect(pollSpy).toHaveBeenCalled();
});

it('calls stop if there is a timeoutHandle and no pending transactions', () => {
const stopSpy = jest.spyOn(smartTransactionsController, 'stop');
smartTransactionsController.timeoutHandle = setInterval(() => ({}));
smartTransactionsController.checkPoll(smartTransactionsController.state);
expect(stopSpy).toHaveBeenCalled();
});
});

describe('poll', () => {
it('does not call updateSmartTransactions on unsupported networks', async () => {
const updateSmartTransactionsSpy = jest.spyOn(
Expand All @@ -276,6 +300,38 @@ describe('SmartTransactionsController', () => {
});
});

describe('trackStxStatusChange', () => {
it('does not track if no prevSmartTransactions', () => {
const smartTransaction = createStateAfterPending()[0];
smartTransactionsController.trackStxStatusChange(
smartTransaction as SmartTransaction,
);
expect(trackMetaMetricsEventSpy).not.toHaveBeenCalled();
});

it('does not track if smartTransaction and prevSmartTransaction have the same status', () => {
const smartTransaction = createStateAfterPending()[0];
smartTransactionsController.trackStxStatusChange(
smartTransaction as SmartTransaction,
smartTransaction as SmartTransaction,
);
expect(trackMetaMetricsEventSpy).not.toHaveBeenCalled();
});

it('tracks status change if smartTransaction and prevSmartTransaction have different statuses', () => {
const smartTransaction = {
...createStateAfterPending()[0],
swapMetaData: {},
};
const prevSmartTransaction = { ...smartTransaction, status: '' };
smartTransactionsController.trackStxStatusChange(
smartTransaction as SmartTransaction,
prevSmartTransaction as SmartTransaction,
);
expect(trackMetaMetricsEventSpy).toHaveBeenCalled();
});
});

describe('setOptInState', () => {
it('sets optIn state', () => {
smartTransactionsController.setOptInState(true);
Expand Down Expand Up @@ -310,6 +366,12 @@ describe('SmartTransactionsController', () => {
});

describe('submitSignedTransactions', () => {
beforeEach(() => {
jest
.spyOn(smartTransactionsController, 'checkPoll')
.mockImplementation(() => ({}));
});

it('submits a smart transaction with signed transactions', async () => {
const signedTransaction = createSignedTransaction();
const signedCanceledTransaction = createSignedCanceledTransaction();
Expand All @@ -332,6 +394,12 @@ describe('SmartTransactionsController', () => {
});

describe('fetchSmartTransactionsStatus', () => {
beforeEach(() => {
jest
.spyOn(smartTransactionsController, 'checkPoll')
.mockImplementation(() => ({}));
});

it('fetches a pending status for a single smart transaction via batchStatus API', async () => {
const uuids = ['uuid1'];
const pendingBatchStatusApiResponse = createPendingBatchStatusApiResponse();
Expand Down Expand Up @@ -463,6 +531,23 @@ describe('SmartTransactionsController', () => {
);
expect(confirmExternalMock).toHaveBeenCalled();
});

it('throws an error if ethersProvider fails', async () => {
smartTransactionsController.ethersProvider.getTransactionReceipt.mockRejectedValueOnce(
'random error' as never,
);
const successfulStx = {
...createStateAfterSuccess()[0],
history: testHistory,
};
try {
await smartTransactionsController.confirmSmartTransaction(
successfulStx as SmartTransaction,
);
} finally {
expect(trackMetaMetricsEventSpy).toHaveBeenCalled();
}
});
});

describe('cancelSmartTransaction', () => {
Expand All @@ -487,4 +572,38 @@ describe('SmartTransactionsController', () => {
expect(configureSpy).toHaveBeenCalledTimes(0);
});
});

describe('getTransactions', () => {
it('retrieves smart transactions by addressFrom and status', () => {
const { smartTransactionsState } = smartTransactionsController.state;
const pendingStx = {
...createStateAfterPending()[0],
history: testHistory,
txParams: {
from: addressFrom,
},
};
smartTransactionsController.update({
smartTransactionsState: {
...smartTransactionsState,
smartTransactions: {
[CHAIN_IDS.ETHEREUM]: [pendingStx] as SmartTransaction[],
},
},
});
const pendingStxs = smartTransactionsController.getTransactions({
addressFrom,
status: SmartTransactionStatuses.PENDING,
});
expect(pendingStxs).toStrictEqual([pendingStx]);
});

it('returns empty array if there are no smart transactions', () => {
const transactions = smartTransactionsController.getTransactions({
addressFrom,
status: SmartTransactionStatuses.PENDING,
});
expect(transactions).toStrictEqual([]);
});
});
});
5 changes: 3 additions & 2 deletions src/SmartTransactionsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ export default class SmartTransactionsController extends BaseController<
SmartTransactionsControllerConfig,
SmartTransactionsControllerState
> {
private timeoutHandle?: NodeJS.Timeout;
public timeoutHandle?: NodeJS.Timeout;

private nonceTracker: any;

private getNetwork: any;

private ethersProvider: any;
public ethersProvider: any;

public txController: any;

Expand Down Expand Up @@ -334,6 +334,7 @@ export default class SmartTransactionsController extends BaseController<
const transactionReceipt = await this.ethersProvider.getTransactionReceipt(
txHash,
);
console.log('transaction receipt', transactionReceipt);
const transaction = await this.ethersProvider.getTransaction(txHash);
const maxFeePerGas = transaction.maxFeePerGas?.toHexString();
const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas?.toHexString();
Expand Down
14 changes: 14 additions & 0 deletions src/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,18 @@ describe('src/utils.js', () => {
);
});
});

describe('getStxProcessingTime', () => {
it('returns undefined if no smartTransactionTimeSubmitted', () => {
expect(utils.getStxProcessingTime(undefined)).toBeUndefined();
});

it('returns 2m and 57s if processing time is 3s ago', () => {
const THREE_SECONDS_AGO = 3 * 1000;
const processingTime = utils.getStxProcessingTime(
Date.now() - THREE_SECONDS_AGO,
);
expect(processingTime).toStrictEqual(3);
});
});
});