From bf722dc64d05fd0b5c43c96662f454c05827ffa3 Mon Sep 17 00:00:00 2001 From: lissavxo Date: Wed, 1 May 2024 01:06:47 -0300 Subject: [PATCH] test: utils --- react/jest.config.js | 17 ++++ .../lib/components/Widget/WidgetContainer.tsx | 15 ++- react/lib/hooks/sound.ts | 15 --- react/lib/tests/util/api-client.test.ts | 95 +++++++++++++++++++ react/lib/tests/util/currency.test.ts | 60 ++++++++++++ react/lib/tests/{ => util}/opReturn.test.ts | 2 +- react/lib/util/opReturn.ts | 10 +- 7 files changed, 188 insertions(+), 26 deletions(-) delete mode 100644 react/lib/hooks/sound.ts create mode 100644 react/lib/tests/util/api-client.test.ts create mode 100644 react/lib/tests/util/currency.test.ts rename react/lib/tests/{ => util}/opReturn.test.ts (99%) diff --git a/react/jest.config.js b/react/jest.config.js index a4166639..a89bbd06 100644 --- a/react/jest.config.js +++ b/react/jest.config.js @@ -6,4 +6,21 @@ module.exports = { collectCoverageFrom: [ 'lib/**/*.{ts,tsx,mjs}', ], + coveragePathIgnorePatterns: [ + "src/*", + "lib/themes", + "lib/tests", + "lib/assets" + ], + coverageThreshold: { + "global": { + "lines": 80 + } + }, + transformIgnorePatterns: [ + 'node_modules/(?!(@babylonjs/core|@babylonjs/loaders)).+.[t|j]sx?$', + ], + moduleNameMapper: { + "^@/(.*)$": "" + } }; diff --git a/react/lib/components/Widget/WidgetContainer.tsx b/react/lib/components/Widget/WidgetContainer.tsx index 36de20a9..6d32b549 100644 --- a/react/lib/components/Widget/WidgetContainer.tsx +++ b/react/lib/components/Widget/WidgetContainer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { isCrypto, @@ -17,7 +17,8 @@ import { import Widget, { WidgetProps } from './Widget'; import { useCustomSnackbar, withSnackbar } from '../../hooks/toast'; -import { useSoundEffects } from '../../hooks/sound'; + +import successSound from '../../assets/success.mp3.json'; export interface WidgetContainerProps extends Omit { @@ -76,12 +77,17 @@ export const WidgetContainer: React.FunctionComponent = const [thisPrice, setThisPrice] = useState(0); const showSnackbar = useCustomSnackbar(); - const { playSuccessSound } = useSoundEffects(); const [success, setSuccess] = useState(false); const [newTxs, setNewTxs] = useState(); const addrType = getCurrencyTypeFromAddress(to); + + const txSound = useMemo( + (): HTMLAudioElement => new Audio(successSound.base64), + [], + ); + if ( !isValidCurrency(currency) || (isCrypto(currency) && addrType !== currency) @@ -104,7 +110,7 @@ export const WidgetContainer: React.FunctionComponent = shouldTriggerOnSuccess(transaction, paymentId, cryptoAmount, opReturn) ) { if (sound) { - playSuccessSound(); + txSound.play().catch(() => {}); } if (!hideToasts) { const toastSuccessText = successText ? successText + ' | ' : ''; @@ -131,6 +137,7 @@ export const WidgetContainer: React.FunctionComponent = to, paymentId, opReturn, + txSound ], ); diff --git a/react/lib/hooks/sound.ts b/react/lib/hooks/sound.ts deleted file mode 100644 index 18caa2c5..00000000 --- a/react/lib/hooks/sound.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useMemo } from 'react'; -import successSound from '../assets/success.mp3.json'; - -export const useSoundEffects = () => { - const successAudio = useMemo((): HTMLAudioElement => new Audio(successSound.base64), - [],); - - const playSuccessSound = () => { - successAudio.play().catch((error) => console.error("Error playing sound:", error)); - }; - - return { - playSuccessSound, - }; -}; diff --git a/react/lib/tests/util/api-client.test.ts b/react/lib/tests/util/api-client.test.ts new file mode 100644 index 00000000..dd1704bc --- /dev/null +++ b/react/lib/tests/util/api-client.test.ts @@ -0,0 +1,95 @@ +import { shouldTriggerOnSuccess } from '../../util/api-client'; +import { Transaction } from '../../util'; + +jest.mock('axios'); + +global.fetch = jest.fn(); + +describe('API Client Util Tests', () => { + describe('shouldTriggerOnSuccess', () => { + it('should return true when all conditions match', () => { + const transaction:Transaction = { + amount: '100.00', + paymentId: '123', + message: 'test message', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, '123', '100.00', 'test message')).toBe(true); + + }); + + it('should return false when the payment ID does not match', () => { + const transaction:Transaction = { + amount: '100.00', + paymentId: '123', + message: 'test opReturn message', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, '999', '100.00', 'test opReturn message')).toBe(false); + }); + + it('should return false when crypto amounts do not match', () => { + const transaction:Transaction = { + amount: '101.00', + paymentId: '123', + message: 'test opReturn message', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, '123', '100.00', 'test opReturn message')).toBe(false); + }); // todo - check if amount higher than the one requested is also valid + + it('should return false when OpReturn data does not match', () => { + const transaction:Transaction = { + amount: '101.00', + paymentId: '123', + message: 'test opReturn message', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, '123', '100.00', 'test opReturn')).toBe(false); + }); + + it('should ignore amount validation when amount is not set', () => { + const transaction:Transaction = { + amount: '101.00', + paymentId: '123', + message: 'test opReturn', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, '123', undefined, 'test opReturn')).toBe(true); + }); + + it('should ignore paymentId validation when paymentId does not exists', () => { + const transaction:Transaction = { + amount: '101.00', + paymentId: '123', + message: 'test opReturn', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, undefined, '101.00', 'test opReturn')).toBe(true); + }); + + it('should ignore opReturn validation when opReturn does not exists', () => { + const transaction:Transaction = { + amount: '101.00', + paymentId: '123', + message: 'test opReturn', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, '123', '101.00', undefined)).toBe(true); + }); + }); +}); diff --git a/react/lib/tests/util/currency.test.ts b/react/lib/tests/util/currency.test.ts new file mode 100644 index 00000000..035091fb --- /dev/null +++ b/react/lib/tests/util/currency.test.ts @@ -0,0 +1,60 @@ +import { isCrypto, isValidCurrency } from '../../util'; + +describe('API Client Util Tests', () => { + describe('isCrypto', () => { + it('recognizes valid crypto currencies', () => { + expect(isCrypto('BCH')).toBeTruthy(); + expect(isCrypto('XEC')).toBeTruthy(); + }); + + it('does not recognize invalid crypto currencies', () => { + expect(isCrypto('BTC')).toBeFalsy(); + expect(isCrypto('ETH')).toBeFalsy(); + }); + + it('is case-sensitive', () => { + expect(isCrypto('bch')).toBeFalsy(); + expect(isCrypto('xec')).toBeFalsy(); + }); + + it('returns false for empty strings', () => { + expect(isCrypto('')).toBeFalsy(); + }); + + it('handles whitespace or special characters', () => { + expect(isCrypto(' BCH ')).toBeFalsy(); + expect(isCrypto('BCH@')).toBeFalsy(); + }); + }); + + describe('isValidCurrency', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('identifies fiat currencies correctly', () => { + expect(isValidCurrency('USD')).toBeTruthy(); + expect(isValidCurrency('EUR')).toBeTruthy(); + }); + + it('identifies crypto currencies correctly', () => { + expect(isValidCurrency('BCH')).toBeTruthy(); + expect(isValidCurrency('XEC')).toBeTruthy(); + }); + + it('returns false for unrecognized currencies', () => { + expect(isValidCurrency('XYZ')).toBeFalsy(); + expect(isValidCurrency('ABC')).toBeFalsy(); + }); + + it('is case-sensitive', () => { + expect(isValidCurrency('bch')).toBeFalsy(); + expect(isValidCurrency('usd')).toBeFalsy(); + }); + + it('handles strings with spaces or special characters', () => { + expect(isValidCurrency(' BCH ')).toBeFalsy(); + expect(isValidCurrency('USD@')).toBeFalsy(); + }); + }); +}); diff --git a/react/lib/tests/opReturn.test.ts b/react/lib/tests/util/opReturn.test.ts similarity index 99% rename from react/lib/tests/opReturn.test.ts rename to react/lib/tests/util/opReturn.test.ts index ecae873e..7dbd9f0e 100644 --- a/react/lib/tests/opReturn.test.ts +++ b/react/lib/tests/util/opReturn.test.ts @@ -2,7 +2,7 @@ import { exportedForTesting, encodeOpReturnProps, EncodeOpReturnParams, -} from '../util/opReturn'; +} from '../../util/opReturn'; const { stringToHex, diff --git a/react/lib/util/opReturn.ts b/react/lib/util/opReturn.ts index 78954f0f..3b9c530d 100644 --- a/react/lib/util/opReturn.ts +++ b/react/lib/util/opReturn.ts @@ -113,19 +113,17 @@ export interface EncodeOpReturnParams { export function encodeOpReturnProps({ opReturn, disablePaymentId, - paymentId, + paymentId }: EncodeOpReturnParams): string { if (opReturn === undefined) { opReturn = ''; } const dataPushdata = getDataPushdata(opReturn, disablePaymentId); - if (paymentId === undefined && !disablePaymentId) { - paymentId = ''; + if (paymentId === undefined || disablePaymentId) { + paymentId = '' } - const pushDataPrefixedPaymentId = prependPaymentIdWithPushdata( - paymentId ?? '', - ); + const pushDataPrefixedPaymentId = prependPaymentIdWithPushdata(paymentId ?? ''); return ( OP_RETURN_PREFIX_PUSHDATA + OP_RETURN_PREFIX +