/ common / features / transaction / selectors.spec.ts
selectors.spec.ts
  1  import BN from 'bn.js';
  2  import cloneDeep from 'lodash/cloneDeep';
  3  
  4  import { Wei } from 'libs/units';
  5  import * as helpers from 'features/helpers';
  6  import * as testHelpers from 'features/testHelpers';
  7  import * as derivedSelectors from 'features/selectors';
  8  import { transactionBroadcastSelectors } from './broadcast';
  9  import { transactionFieldsSelectors } from './fields';
 10  import { transactionMetaSelectors } from './meta';
 11  import { transactionNetworkTypes, transactionNetworkSelectors } from './network';
 12  import { transactionSignSelectors } from './sign';
 13  import * as selectors from './selectors';
 14  
 15  const initialState = cloneDeep(testHelpers.getInitialState());
 16  
 17  describe('helpers selector', () => {
 18    const state = testHelpers.getInitialState();
 19    state.transaction = {
 20      ...state.transaction,
 21      meta: {
 22        ...state.transaction.meta,
 23        unit: 'ETH'
 24      },
 25      fields: {
 26        to: {
 27          raw: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
 28          value: new Buffer([0, 1, 2, 3])
 29        },
 30        data: {
 31          raw: '',
 32          value: null
 33        },
 34        nonce: {
 35          raw: '0',
 36          value: new BN('0')
 37        },
 38        value: {
 39          raw: '1000000000',
 40          value: Wei('1000000000')
 41        },
 42        gasLimit: {
 43          raw: '21000',
 44          value: Wei('21000')
 45        },
 46        gasPrice: {
 47          raw: '1500',
 48          value: Wei('1500')
 49        }
 50      }
 51    };
 52  
 53    it('should reduce the fields state to its base values', () => {
 54      const values = {
 55        data: null,
 56        gasLimit: Wei('21000'),
 57        gasPrice: Wei('1500'),
 58        nonce: new BN('0'),
 59        to: new Buffer([0, 1, 2, 3]),
 60        value: Wei('1000000000')
 61      };
 62      expect(helpers.reduceToValues(state.transaction.fields)).toEqual(values);
 63    });
 64  
 65    it('should check isFullTransaction with full transaction arguments', () => {
 66      const currentTo = derivedSelectors.getCurrentTo(state);
 67      const currentValue = derivedSelectors.getCurrentValue(state);
 68      const transactionFields = transactionFieldsSelectors.getFields(state);
 69      const unit = derivedSelectors.getUnit(state);
 70      const dataExists = selectors.getDataExists(state);
 71      const validGasCost = derivedSelectors.getValidGasCost(state);
 72      const isFullTransaction = helpers.isFullTx(
 73        state,
 74        transactionFields,
 75        currentTo,
 76        currentValue,
 77        dataExists,
 78        validGasCost,
 79        unit
 80      );
 81      expect(isFullTransaction).toEqual(true);
 82    });
 83  
 84    it('should check isFullTransaction without full transaction arguments', () => {
 85      const currentTo = { raw: '', value: null };
 86      const currentValue = derivedSelectors.getCurrentValue(state);
 87      const transactionFields = transactionFieldsSelectors.getFields(state);
 88      const unit = derivedSelectors.getUnit(state);
 89      const dataExists = selectors.getDataExists(state);
 90      const validGasCost = derivedSelectors.getValidGasCost(state);
 91      const isFullTransaction = helpers.isFullTx(
 92        state,
 93        transactionFields,
 94        currentTo,
 95        currentValue,
 96        dataExists,
 97        validGasCost,
 98        unit
 99      );
100      expect(isFullTransaction).toEqual(false);
101    });
102  });
103  
104  //#region Broadcast
105  describe('broadcast selector', () => {
106    const state = testHelpers.getInitialState();
107    state.transaction = {
108      ...state.transaction,
109      broadcast: {
110        ...state.transaction.broadcast,
111        testIndexingHash1: {
112          broadcastedHash: 'testBroadcastedHash',
113          broadcastSuccessful: true,
114          isBroadcasting: false,
115          serializedTransaction: new Buffer([1, 2, 3])
116        },
117        testIndexingHash2: {
118          broadcastedHash: 'testBroadcastedHash',
119          broadcastSuccessful: true,
120          isBroadcasting: false,
121          serializedTransaction: new Buffer([1, 2, 3])
122        }
123      },
124      sign: {
125        ...state.transaction.sign,
126        indexingHash: 'testIndexingHash1',
127        pending: false
128      }
129    };
130    it('should check getTransactionState with an indexing hash', () => {
131      expect(transactionBroadcastSelectors.getTransactionStatus(state, 'testIndexingHash1')).toEqual(
132        state.transaction.broadcast.testIndexingHash1
133      );
134    });
135  
136    it('should check getCurrentTransactionStatus', () => {
137      expect(selectors.getCurrentTransactionStatus(state)).toEqual(
138        state.transaction.broadcast.testIndexingHash2
139      );
140    });
141  
142    it('should check currentTransactionFailed', () => {
143      expect(selectors.currentTransactionFailed(state)).toEqual(false);
144    });
145  
146    it('should check currentTransactionBroadcasting', () => {
147      expect(selectors.currentTransactionBroadcasting(state)).toEqual(false);
148    });
149  
150    it('should check currentTransactionBroadcasted', () => {
151      expect(selectors.currentTransactionBroadcasted(state)).toEqual(true);
152    });
153  
154    it('should return false on getCurrentTransactionStatus if no index hash present', () => {
155      state.transaction.sign.indexingHash = null;
156      expect(selectors.getCurrentTransactionStatus(state)).toEqual(false);
157    });
158  });
159  //#endregion Broadcast
160  
161  //#region Current
162  describe('current selector', () => {
163    const state = testHelpers.getInitialState();
164    state.transaction = {
165      ...state.transaction,
166      fields: {
167        ...state.transaction.fields,
168        to: {
169          raw: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
170          value: new Buffer([0, 1, 2, 3])
171        },
172        gasLimit: {
173          raw: '21000',
174          value: Wei('21000')
175        },
176        gasPrice: {
177          raw: '1500',
178          value: Wei('1500')
179        }
180      },
181      meta: {
182        ...state.transaction.meta,
183        unit: 'ETH',
184        previousUnit: 'ETH'
185      }
186    };
187  
188    it('should get stored receiver address on getCurrentTo', () => {
189      expect(derivedSelectors.getCurrentTo(state)).toEqual(state.transaction.fields.to);
190    });
191  
192    it('should get stored value on getCurrentValue', () => {
193      expect(derivedSelectors.getCurrentValue(state)).toEqual(state.transaction.fields.value);
194    });
195  
196    it('should get message to the receiver', () => {
197      expect(derivedSelectors.getCurrentToAddressMessage(state)).toEqual({
198        msg: 'Thank you for donating to MyCrypto. TO THE MOON!'
199      });
200    });
201  
202    it('should check isValidGasPrice', () => {
203      expect(selectors.isValidGasPrice(state)).toEqual(true);
204    });
205  
206    it('should check isEtherTransaction', () => {
207      expect(derivedSelectors.isEtherTransaction(state)).toEqual(true);
208    });
209  
210    it('should check isValidGasLimit', () => {
211      expect(selectors.isValidGasLimit(state)).toEqual(true);
212    });
213  
214    it('should check isValidCurrentTo', () => {
215      expect(derivedSelectors.isValidCurrentTo(state)).toEqual(true);
216    });
217  
218    it('should check isCurrentToLabelEntry', () => {
219      expect(derivedSelectors.isCurrentToLabelEntry(state)).toEqual(false);
220  
221      const otherState = { ...state };
222      otherState.transaction = {
223        ...state.transaction,
224        fields: { ...state.transaction.fields, to: { ...state.transaction.fields.to, raw: 'derp' } }
225      };
226  
227      expect(derivedSelectors.isCurrentToLabelEntry(otherState)).toEqual(true);
228    });
229  });
230  
231  //#endregion Current
232  
233  //#region Fields
234  describe('fields selector', () => {
235    const state = testHelpers.getInitialState();
236    state.transaction.fields = {
237      to: {
238        raw: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
239        value: new Buffer([0, 1, 2, 3])
240      },
241      data: {
242        raw: '',
243        value: null
244      },
245      nonce: {
246        raw: '0',
247        value: new BN('0')
248      },
249      value: {
250        raw: '1000000000',
251        value: Wei('1000000000')
252      },
253      gasLimit: {
254        raw: '21000',
255        value: Wei('21000')
256      },
257      gasPrice: {
258        raw: '1500',
259        value: Wei('1500')
260      }
261    };
262  
263    it('should get fields from fields store', () => {
264      expect(transactionFieldsSelectors.getFields(state)).toEqual(state.transaction.fields);
265    });
266  
267    it('should get data from fields store', () => {
268      expect(transactionFieldsSelectors.getData(state)).toEqual(state.transaction.fields.data);
269    });
270  
271    it('should get gas limit from fields store', () => {
272      expect(transactionFieldsSelectors.getGasLimit(state)).toEqual(
273        state.transaction.fields.gasLimit
274      );
275    });
276  
277    it('should get value from fields store', () => {
278      expect(transactionFieldsSelectors.getValue(state)).toEqual(state.transaction.fields.value);
279    });
280  
281    it('sould get receiver address from fields store', () => {
282      expect(transactionFieldsSelectors.getTo(state)).toEqual(state.transaction.fields.to);
283    });
284  
285    it('should get nonce from fields store', () => {
286      expect(transactionFieldsSelectors.getNonce(state)).toEqual(state.transaction.fields.nonce);
287    });
288  
289    it('should get gas price from fields store', () => {
290      expect(transactionFieldsSelectors.getGasPrice(state)).toEqual(
291        state.transaction.fields.gasPrice
292      );
293    });
294  
295    it('should check getDataExists', () => {
296      expect(selectors.getDataExists(state)).toEqual(false);
297    });
298  
299    it('should check when gas cost is valid', () => {
300      expect(derivedSelectors.getValidGasCost(state)).toEqual(true);
301    });
302  
303    it('should check when gas cost is invalid', () => {
304      state.wallet.balance = {
305        wei: Wei('0'),
306        isPending: false
307      };
308      expect(derivedSelectors.getValidGasCost(state)).toEqual(false);
309    });
310  });
311  
312  //#endregion Fields
313  
314  //#region Meta
315  describe('meta tests', () => {
316    const state = { ...initialState };
317    (state.transaction.meta = {
318      unit: 'ETH',
319      previousUnit: 'ETH',
320      decimal: 18,
321      tokenValue: {
322        raw: '',
323        value: null
324      },
325      tokenTo: {
326        raw: '',
327        value: null
328      },
329      from: 'fromAddress',
330      isContractInteraction: false
331    }),
332      (state.customTokens = [
333        {
334          address: '0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7',
335          symbol: 'UNI',
336          decimal: 0
337        }
338      ]);
339  
340    it('should get the stored sender address', () => {
341      expect(derivedSelectors.getFrom(state)).toEqual(state.transaction.meta.from);
342    });
343  
344    it('should get the stored decimal', () => {
345      expect(transactionMetaSelectors.getDecimal(state)).toEqual(state.transaction.meta.decimal);
346    });
347  
348    it('should get the token value', () => {
349      expect(transactionMetaSelectors.getTokenValue(state)).toEqual(
350        state.transaction.meta.tokenValue
351      );
352    });
353  
354    it('should get the token receiver address', () => {
355      expect(transactionMetaSelectors.getTokenTo(state)).toEqual(state.transaction.meta.tokenTo);
356    });
357  
358    it('should get the stored unit', () => {
359      expect(derivedSelectors.getUnit(state)).toEqual(state.transaction.meta.unit);
360    });
361  
362    it('should get the stored previous unit', () => {
363      expect(selectors.getPreviousUnit(state)).toEqual(state.transaction.meta.previousUnit);
364    });
365  
366    it('should get the decimal for ether', () => {
367      expect(derivedSelectors.getDecimalFromUnit(state, derivedSelectors.getUnit(state))).toEqual(18);
368    });
369  
370    it('should get the decimal for a token', () => {
371      expect(derivedSelectors.getDecimalFromUnit(state, 'UNI')).toEqual(0);
372    });
373  
374    it('should throw error if the token is not found', () => {
375      expect(() => derivedSelectors.getDecimalFromUnit(state, 'ABC')).toThrowError(
376        `Token ABC not found`
377      );
378    });
379  });
380  
381  //#endregion Meta
382  
383  //#region Network
384  describe('network selector', () => {
385    const state = testHelpers.getInitialState();
386    state.transaction.network = {
387      ...state.transaction.network,
388      gasEstimationStatus: transactionNetworkTypes.RequestStatus.REQUESTED,
389      getFromStatus: transactionNetworkTypes.RequestStatus.SUCCEEDED,
390      getNonceStatus: transactionNetworkTypes.RequestStatus.REQUESTED,
391      gasPriceStatus: transactionNetworkTypes.RequestStatus.SUCCEEDED
392    };
393  
394    it('should get network status', () => {
395      expect(transactionNetworkSelectors.getNetworkStatus(state)).toEqual(state.transaction.network);
396    });
397  
398    it('should check with the store if the nonce request is pending', () => {
399      expect(transactionNetworkSelectors.nonceRequestPending(state)).toEqual(true);
400    });
401  
402    it('should check with the store if the nonce request failed', () => {
403      state.transaction.network.getNonceStatus = transactionNetworkTypes.RequestStatus.FAILED;
404      expect(transactionNetworkSelectors.nonceRequestFailed(state)).toEqual(true);
405    });
406  
407    it('should check with the store if the gas estimation is pending', () => {
408      expect(transactionNetworkSelectors.getGasEstimationPending(state)).toEqual(true);
409    });
410  
411    it('should check with the store if gas limit estimation timed out', () => {
412      state.transaction.network.gasEstimationStatus = transactionNetworkTypes.RequestStatus.TIMEDOUT;
413      expect(transactionNetworkSelectors.getGasLimitEstimationTimedOut(state)).toEqual(true);
414    });
415  
416    it('should check with the store if network request is pending', () => {
417      state.transaction.network.gasEstimationStatus = transactionNetworkTypes.RequestStatus.REQUESTED;
418      expect(transactionNetworkSelectors.isNetworkRequestPending(state)).toEqual(true);
419    });
420  });
421  
422  //#endregion Network
423  
424  //#region Sign
425  describe('sign tests', () => {
426    const state = testHelpers.getInitialState();
427    (state.transaction.sign = {
428      indexingHash: 'testIndexingHash',
429      pending: false,
430      local: {
431        signedTransaction: new Buffer([4, 5, 6, 7])
432      },
433      web3: {
434        transaction: null
435      }
436    }),
437      it('should return whether the current signature is pending', () => {
438        expect(derivedSelectors.signaturePending(state)).toEqual({
439          isHardwareWallet: false,
440          isSignaturePending: false
441        });
442      });
443  
444    it('should should get the stored sign state', () => {
445      expect(transactionSignSelectors.getSignState(state)).toEqual(state.transaction.sign);
446    });
447  
448    it('should get the signed local transaction state', () => {
449      expect(transactionSignSelectors.getSignedTx(state)).toEqual(
450        state.transaction.sign.local.signedTransaction
451      );
452    });
453  
454    it('should get the signed web3 transaction state', () => {
455      expect(transactionSignSelectors.getWeb3Tx(state)).toEqual(
456        state.transaction.sign.web3.transaction
457      );
458    });
459  
460    it('should get the serialized transaction state', () => {
461      expect(derivedSelectors.getSerializedTransaction(state)).toEqual(new Buffer([4, 5, 6, 7]));
462    });
463  });
464  
465  //#endregion Sign