From 7d277a99f361272c4fd0a5ec6befbbe9c40dd6b4 Mon Sep 17 00:00:00 2001 From: Rostik Kayko <119863957+rostislav-deriv@users.noreply.github.com> Date: Wed, 17 May 2023 06:55:44 +0300 Subject: [PATCH] Rostislav / WALL-243 / TS migration Cashier (#7746) * refactor: migration done * refactor: init pr * Rostislav / 85420 / Migrate cashier `utils` to TS (#2) * refactor: init pr * refactor: refactored utils/validator to ts + moved it to shared * refactor: added more typescript shenanigans to validator * refactor: a minor change * refactor: import refactored * refactor: some more progress with the validator to avoid the build failing hopefully * refactor: validator ts issues fixed * refactor: more ts fixes to get this to work * refactor: removed validator from cashier * refactor: added tests for validator/errors in shared * refactor: applied suggested changes * refactor: some more any -> unknown refactoring * refactor: fixed cringe mistake * refactor: fixed a small mistake * Update packages/cashier/src/utils/server_time.ts Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> --------- Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * Rostislav / 85418 / Migrate disable-withdrawal-modal Component (#4) * refactor: ts migration * refactor: rmved the file * Rostislav / 85421 / Migrate cashier constants to TS (#3) * refactor: init pr * refactor: constants.js migrated * refactor: tsx'd transaction-status.js * refactor: tsx'd static-url.jsx * refactor: test deploy again * refactor: tests fixed * refactor: import fixed * refactor: removed the usage of PlatformContext and its is_appstore from StaticUrl * refactor: added eslint-disable-next-line * refactor: removed "return undefined" and typed return value * refactor: refactored types out into type file * refactor: added more constants * refactor: removed ! from recent-transaction.tsx * refactor: removed ts-ignore * refactor: more importing types instead of defining whats defined * refactor: fixed tests * refactor: capitalized status codes * refactor: rollbacked status codes in tests some more (lowercase -> CAPS) * refactor: removed unused import * refactor: Types -> ../types * refactor: added "as const" in constants.ts * refactor: new typeguards + improved readability * Update packages/cashier/src/constants/transaction-status.tsx Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * refactor: fixed typeguards * refactor: type fixes * refactor: mockRootStore type changed to TStores (@hamid-deriv's suggestion) * refactor: added default return for transaction status --------- Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * Rostislav / WALL-257 / TS migration of Payment Agent Transfer UI component and store (#7) * refactor: init pr * refactor: migrated the store * refactor: migrated components * refactor: fixed mockStore * refactor: minor fix * refactor: more minor fixes * refactor: a few more minor fixes * refactor: one more import fix * refactor: tests fix * refactor: pa transfer request amount type string -> number * Update packages/cashier/src/types/props.types.ts Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * refactor: applied @farzin-deriv's suggestion * refactor: applied more suggestions --------- Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * Rostislav / WALL-256 / TS migration of payment agent UI component and store (#6) * refactor: init pr * refactor: pa deposit details * refactor: mad webstorm suggestion (lets have it like this for now) * refactor: pa card helpers * refactor: pa card helpers (+ this one) * refactor: pa detail + spec * refactor: pa detail index * refactor: pa + spec * refactor: pa card deposit details spec * refactor: pa card listed withdraw form (to revisit) * refactor: pa card listed withdraw form (index) * refactor: pa card description * refactor: added types * refactor: types export * refactor: pa search box * refactor: pa withdraw form * refactor: pa card (to revisit) * refactor: pa container spec * refactor: pa container (to revisit) * refactor: pa container index * refactor: pa container index +1 * refactor: pa receipt index * refactor: pa index * refactor: pa withdraw confirm index * refactor: pa receipt (revisit after pa store) * refactor: pa receipt spec +1 * refactor: pa listed withdraw form * refactor: pa withdraw confirm + spec (to revisit after pa store) * refactor: pa listed withdraw form spec * refactor: pa disclaimer * refactor: pa receipt (revisit after pa store) * refactor: side note * refactor: stores types updated * refactor: store refactored + types added + few more changes * refactor: logic changed + store tests fixed * refactor: tests fixed it seems * fix: mockStore fixed * refactor: param type simplified for getNormalizedPaymentMethod * refactor: removed unused scss + minor changes * refactor: payment-agent.spec.tsx final * refactor: simplified payment-agent-card.tsx a bit * refactor: pa container spec removed TRootStore * refactor: a few changes to tests + a few ts errors resolved * refactor: init pr * refactor: init pr * refactor: trigger deploy * refactor: trigger deploy * refactor: old logic for banks and payment agents restored in PA store + type alias removed * Update packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/payment-agent-withdrawal-locked.tsx Co-authored-by: George Usynin <103181646+heorhi-deriv@users.noreply.github.com> * Update packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/payment-agent-withdrawal-locked.tsx Co-authored-by: George Usynin <103181646+heorhi-deriv@users.noreply.github.com> * refactor: changed `undefined` fallback to '' in pa listed withdraw form * refactor: renamed the types to start with 'T' * refactor: tests should be fixed now * refactor: one more tests fix * refactor: ReactNode -> React.ReactNode * refactor: a minor change * refactor: tests re-run check * refactor: a minor change * Update packages/stores/types.ts Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * Update packages/stores/src/mockStore.ts Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * refactor: more suggestions applied * refactor: suggestions * refactor: React.ReactNode | string => React.ReactNode * refactor: PaymentAgentCard default values set for boolean params * refactor: formatting post merge-conflict problem fixed * refactor: verification_code removed from props and is being set from client store * refactor: prev commit changes fix in tests (oops) * refactor: one more test fix * refactor: tests fix * refactor: cashier store ts fix * refactor: type problems in PaymentAgentCard fixed with the help of @heorhi-deriv * Update packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/payment-agent-withdraw-confirm.tsx Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * fix: tests fix --------- Co-authored-by: George Usynin <103181646+heorhi-deriv@users.noreply.github.com> Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * Rostislav / WALL-261 / Migrate remaining cashier js to ts (#8) * refactor: init pr * refactor: refactored app to ts * refactor: cashier pages index.js -> .ts * refactor: remove a bunch of import aliases * refactor: revert accidental committed changes * refactor: some changes to the request types * refactor: sonarcloud fixes + removing TRootStore * refactor: continue removing TRootStore from everywhere * refactor: remove redundant static typing for mockStore call results + continue removing TRootStore * refactor: remove TRootStore completely * refactor: const mockRootStore -> const mock_root_store (convention for consts) * refactor: more spec refactoring * refactor: remove TRootStore from non-specs as well * fix: tests fix * refactor: removed imports for beforeEach * refactor: remove redundant cast * fix: fixing tests * fix: fixing tests * fix: fixing tests * fix: fix tests * refactor: a bunch of errors resolved * fix: test fix * fix: cast amount to number * fix: a few more forgotten number casts * fix: fixing tests * fix: fixing code smells * fix: fixing code smells * fix: fixing code smells * refactor: remove import of a removed type * refactor: rolled back on bad refactor + +{smth} -> Number({smth}) * refactor: ref and ReactPortal problems resolved * refactor: pa withdraw form spec type errors resolved * refactor: more refactoring in cashier tests * fix: tests fix * Update packages/shared/src/utils/validator/errors.ts Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * Update packages/components/src/components/input/input.tsx Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * refactor: revert back to TRootStore for a bunch of stores * refactor: TCoreStores changed + withdraw store change revert * refactor: Array -> T[] * refactor: PA store param type: TStores -> TRootStore * refactor: crypto-fiat-converter.spec.tsx type errors resolved * refactor: crypto transactions ts changes * Update packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-renderer.spec.tsx Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * Update packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-renderer.spec.tsx Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * Update packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-renderer.spec.tsx Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * refactor: error.tsx and error.spec.tsx type errors resolved * refactor: recent-transaction.tsx status null return value accounted for * refactor: crypto-transactions-renderer.spec.tsx removed unused import * refactor: error.tsx props type adjusted * refactor: payment-agent-store.ts added await on async call * refactor: Array => T[] in more cashier files + account-transfer-store.ts type error resolution * refactor: TWebsocketCall functions types made async * Update packages/stores/types.ts Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> * fix: pa transfer store tests oddity fixed * fix: @farzin-deriv's suggestions * fix: another suggestion applied * fix: another suggestion applied * refactor: TStores-related refactoring * refactor: TStores-related refactoring * refactor: ClientStore.account_limits.daily_transfers types changed * fix: mockStore account_limits type inconsistency * refactor: fixing more ts errors * refactor: TClientStore account_limits type now used from api types * refactor: some of the more risky changes * fix: tests fix * refactor: accidental auto change rollback * refactor: remove auto changes * refactor: remove auto changes * refactor: ErrorDialog-related type errors fixed * refactor: withdraw.tsx-related type errors fixed * refactor: withdrawal-locked.spec.tsx-related type errors resolved * refactor: more type errors resolved * refactor: package-lock.json set to master * refactor: remove icons.js from changes * refactor: remve type errors from pa store and pa transfer store * refactor: minor refactoring * refactor: minor refactoring * refactor: removing more type errors * refactor: insignificant change reverted * refactor: { [key: string]: never } -> Record * refactor: small type change in tests * refactor: got rid of a bunch of code smells * refactor: got rid of a bunch more code smells * refactor: got rid of a bunch more code smells * fix: tests fix * fix: qa fix * fix: tests fix --------- Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com> Co-authored-by: George Usynin <103181646+heorhi-deriv@users.noreply.github.com> Co-authored-by: hirad-deriv --- .../src/components/routes/routes-wrapper.tsx | 6 +- packages/appstore/src/types/props.types.ts | 16 - .../account-platform-icon.tsx | 2 +- .../__tests__/account-prompt-dialog.spec.tsx | 4 +- .../real/__tests__/real.spec.tsx | 18 +- .../cashier-onboarding-side-note.spec.tsx | 9 +- .../__tests__/cashier-onboarding.spec.tsx | 14 +- ...ox.spec.js => cashier-search-box.spec.tsx} | 2 + ...-search-box.jsx => cashier-search-box.tsx} | 42 +-- .../components/cashier-search-box/index.js | 3 - .../components/cashier-search-box/index.ts | 3 + .../__tests__/crypto-fiat-converter.spec.tsx | 12 +- .../crypto-fiat-converter.tsx | 9 +- .../crypto-transactions-cancel-modal.spec.tsx | 8 +- .../crypto-transactions-history.spec.tsx | 7 +- .../crypto-transactions-renderer.spec.tsx | 12 +- .../crypto-transactions-status-modal.spec.tsx | 7 +- .../crypto-transactions-renderer.tsx | 127 ++++---- .../email-verification-empty-state.test.tsx | 8 +- .../email-verification-empty-state.tsx | 4 +- .../components/empty-state/empty-state.tsx | 7 +- .../__tests__/error-dialog.spec.tsx | 55 ++-- .../components/error-dialog/error-dialog.tsx | 2 +- .../components/error/__tests__/error.spec.tsx | 23 +- .../cashier/src/components/error/error.tsx | 7 +- .../percentage-selector.tsx | 2 +- .../read-more-wrapper/read-more-wrapper.tsx | 2 +- .../__tests__/recent-transaction.spec.tsx | 9 +- .../recent-transaction/recent-transaction.tsx | 57 ++-- .../src/components/side-note/side-note.tsx | 7 +- .../__tests__/transfer-confirm.spec.tsx | 43 +-- .../transfer-confirm/transfer-confirm.tsx | 8 +- .../constants/{constants.js => constants.ts} | 8 +- ...ction-status.js => transaction-status.tsx} | 40 ++- .../cashier/__tests__/cashier.spec.tsx | 56 ++-- .../src/containers/cashier/cashier.tsx | 2 +- .../__tests__/route-with-sub-routes.spec.tsx | 1 + .../routes/__tests__/routes.spec.tsx | 18 +- .../__tests__/error-component.spec.tsx | 2 +- .../error-component/error-component.tsx | 6 +- .../routes/route-with-sub-routes.tsx | 7 +- .../__tests__/account-transfer.spec.tsx | 6 +- .../__tests__/account-transfer-form.spec.tsx | 16 +- .../account-transfer-form-side-note.tsx | 7 +- .../account-transfer-form.tsx | 31 +- .../account-transfer-locked.spec.tsx | 7 +- .../account-transfer-no-account.spec.tsx | 4 +- .../account-transfer-receipt.spec.tsx | 7 +- .../pages/deposit/__tests__/deposit.spec.tsx | 41 ++- .../__tests__/crypto-deposit.spec.tsx | 72 ++--- .../deposit/crypto-deposit/crypto-deposit.tsx | 10 +- .../__tests__/deposit-locked.spec.tsx | 34 +- .../cashier/src/pages/{index.js => index.ts} | 0 .../pages/on-ramp/__tests__/on-ramp.spec.tsx | 93 ++++-- .../__tests__/on-ramp-provider-card.spec.tsx | 32 +- .../__tests__/on-ramp-provider-popup.spec.tsx | 102 ++---- .../cashier/src/pages/on-ramp/on-ramp.tsx | 3 +- .../__tests__/payment-agent-transfer.spec.tsx | 4 +- .../src/pages/payment-agent-transfer/index.js | 3 - .../src/pages/payment-agent-transfer/index.ts | 3 + ...> payment-agent-transfer-confirm.spec.tsx} | 13 +- .../{index.js => index.ts} | 2 +- ...jsx => payment-agent-transfer-confirm.tsx} | 14 +- ...s => payment-agent-transfer-form.spec.tsx} | 39 ++- .../{index.js => index.ts} | 2 +- ...rm.jsx => payment-agent-transfer-form.tsx} | 87 ++++-- ...> payment-agent-transfer-receipt.spec.tsx} | 13 +- .../{index.js => index.ts} | 2 +- ...jsx => payment-agent-transfer-receipt.tsx} | 23 +- ...ransfer.jsx => payment-agent-transfer.tsx} | 2 +- .../__tests__/payment-agent.spec.tsx | 4 +- .../cashier/src/pages/payment-agent/index.js | 3 - .../cashier/src/pages/payment-agent/index.ts | 3 + ...> payment-agent-card-description.spec.tsx} | 19 +- .../__tests__/payment-agent-card.spec.js | 16 - .../__tests__/payment-agent-card.spec.tsx | 33 ++ .../{heplers.spec.js => helpers.spec.tsx} | 2 +- .../payment-agent-card/helpers/helpers.js | 18 -- .../payment-agent-card/helpers/helpers.ts | 21 ++ .../helpers/{index.js => index.ts} | 0 .../payment-agent/payment-agent-card/index.js | 3 - .../payment-agent/payment-agent-card/index.ts | 3 + ...jsx => payment-agent-card-description.tsx} | 14 +- ...-agent-card.jsx => payment-agent-card.tsx} | 18 +- ...ec.js => payment-agent-container.spec.tsx} | 37 ++- .../payment-agent-container/index.js | 3 - .../payment-agent-container/index.ts | 3 + ...tainer.jsx => payment-agent-container.tsx} | 26 +- ...yment-agent-card-deposit-details.spec.tsx} | 9 +- .../{index.js => index.ts} | 2 +- ....jsx => payment-agent-deposit-details.tsx} | 20 +- ....spec.js => payment-agent-detail.spec.tsx} | 2 +- .../payment-agent-detail/index.js | 3 - .../payment-agent-detail/index.ts | 3 + ...nt-detail.jsx => payment-agent-detail.tsx} | 42 +-- .../payment-agent-details.scss | 44 --- .../payment-agent-disclaimer/index.js | 3 - .../payment-agent-disclaimer/index.ts | 3 + ...aimer.jsx => payment-agent-disclaimer.tsx} | 4 +- .../payment-agent-list/withdrawal-tab.tsx | 3 +- ...yment-agent-listed-withdraw-form.spec.tsx} | 50 +-- .../{index.js => index.ts} | 2 +- ...=> payment-agent-listed-withdraw-form.tsx} | 64 ++-- ...spec.js => payment-agent-receipt.spec.tsx} | 13 +- .../payment-agent-receipt/index.js | 3 - .../payment-agent-receipt/index.ts | 3 + ...-receipt.jsx => payment-agent-receipt.tsx} | 45 +-- .../payment-agent-search-box/index.js | 3 - .../payment-agent-search-box/index.ts | 3 + ...h-box.jsx => payment-agent-search-box.tsx} | 2 +- ...s => payment-agent-withdraw-form.spec.tsx} | 50 +-- .../{index.js => index.ts} | 2 +- ... payment-agent-unlisted-withdraw-form.tsx} | 55 ++-- ...> payment-agent-withdraw-confirm.spec.tsx} | 23 +- .../{index.js => index.ts} | 2 +- ...jsx => payment-agent-withdraw-confirm.tsx} | 18 +- .../{index.js => index.ts} | 2 +- ...sx => payment-agent-withdrawal-locked.tsx} | 35 ++- .../{payment-agent.jsx => payment-agent.tsx} | 11 +- .../withdrawal/__tests__/withdrawal.spec.tsx | 42 +-- .../__tests__/crypto-withdraw-form.spec.tsx | 7 +- .../crypto-withdraw-receipt.spec.tsx | 7 +- .../withdraw/__tests__/withdraw.spec.tsx | 7 +- .../__tests__/withdrawal-locked.spec.tsx | 30 +- .../account-prompt-dialog-store.spec.ts | 8 +- .../__tests__/account-transfer-store.spec.ts | 58 ++-- .../crypto-fiat-converter-store.spec.ts | 9 +- .../stores/__tests__/deposit-store.spec.ts | 7 +- .../stores/__tests__/general-store.spec.ts | 22 +- .../src/stores/__tests__/iframe-store.spec.ts | 11 +- .../stores/__tests__/on-ramp-store.spec.ts | 79 ++--- ...e.spec.js => payment-agent-store.spec.tsx} | 294 ++++++++++-------- ...s => payment-agent-transfer-store.spec.ts} | 73 +++-- .../transaction-history-store.spec.ts | 24 +- .../stores/__tests__/withdraw-store.spec.ts | 25 +- .../src/stores/account-prompt-dialog-store.ts | 2 +- .../src/stores/account-transfer-store.ts | 6 +- packages/cashier/src/stores/base-store.ts | 19 +- packages/cashier/src/stores/cashier-store.ts | 4 +- .../src/stores/crypto-fiat-converter-store.ts | 2 +- packages/cashier/src/stores/error-store.ts | 2 +- packages/cashier/src/stores/general-store.ts | 8 +- packages/cashier/src/stores/on-ramp-store.ts | 32 +- ...-agent-store.js => payment-agent-store.ts} | 152 +++++---- ...ore.js => payment-agent-transfer-store.ts} | 66 ++-- .../src/stores/transaction-history-store.ts | 12 +- packages/cashier/src/stores/withdraw-store.ts | 2 +- .../types/crypto-transaction-details.types.ts | 6 +- packages/cashier/src/types/index.ts | 1 + .../cashier/src/types/payment-agent.types.ts | 67 ++++ packages/cashier/src/types/props.types.ts | 9 +- .../cashier/src/types/root-store.types.ts | 2 +- .../cashier/src/types/transactions.types.ts | 20 +- packages/cashier/src/types/websocket.types.ts | 53 +++- .../utils/{server_time.js => server_time.ts} | 17 +- packages/cashier/src/utils/utility.js | 117 ------- packages/cashier/src/utils/utility.ts | 43 +++ packages/cashier/src/utils/validator/index.js | 1 - .../cashier/src/utils/validator/validator.js | 112 ------- .../expansion-panel/expansion-panel.tsx | 2 +- .../components/src/components/input/input.tsx | 4 +- packages/shared/src/index.ts | 1 + .../utils/validator/__tests__/error.spec.tsx | 44 +++ .../src/utils/validator/errors.ts} | 10 +- packages/shared/src/utils/validator/index.ts | 3 + .../shared/src/utils/validator/validator.ts | 141 +++++++++ packages/stores/src/mockStore.ts | 14 +- packages/stores/types.ts | 32 +- 168 files changed, 2047 insertions(+), 1759 deletions(-) rename packages/cashier/src/components/cashier-search-box/__tests__/{cashier-search-box.spec.js => cashier-search-box.spec.tsx} (98%) rename packages/cashier/src/components/cashier-search-box/{cashier-search-box.jsx => cashier-search-box.tsx} (71%) delete mode 100644 packages/cashier/src/components/cashier-search-box/index.js create mode 100644 packages/cashier/src/components/cashier-search-box/index.ts rename packages/cashier/src/constants/{constants.js => constants.ts} (99%) rename packages/cashier/src/constants/{transaction-status.js => transaction-status.tsx} (78%) rename packages/cashier/src/pages/{index.js => index.ts} (100%) delete mode 100644 packages/cashier/src/pages/payment-agent-transfer/index.js create mode 100644 packages/cashier/src/pages/payment-agent-transfer/index.ts rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/__tests__/{payment-agent-transfer-confirm.spec.js => payment-agent-transfer-confirm.spec.tsx} (92%) rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/{index.js => index.ts} (87%) rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/{payment-agent-transfer-confirm.jsx => payment-agent-transfer-confirm.tsx} (79%) rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/__tests__/{payment-agent-transfer-form.spec.js => payment-agent-transfer-form.spec.tsx} (73%) rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/{index.js => index.ts} (91%) rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/{payment-agent-transfer-form.jsx => payment-agent-transfer-form.tsx} (67%) rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/__tests__/{payment-agent-transfer-receipt.spec.js => payment-agent-transfer-receipt.spec.tsx} (83%) rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/{index.js => index.ts} (87%) rename packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/{payment-agent-transfer-receipt.jsx => payment-agent-transfer-receipt.tsx} (83%) rename packages/cashier/src/pages/payment-agent-transfer/{payment-agent-transfer.jsx => payment-agent-transfer.tsx} (98%) delete mode 100644 packages/cashier/src/pages/payment-agent/index.js create mode 100644 packages/cashier/src/pages/payment-agent/index.ts rename packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/{payment-agent-card-description.spec.js => payment-agent-card-description.spec.tsx} (63%) delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card.spec.js create mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card.spec.tsx rename packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/__tests__/{heplers.spec.js => helpers.spec.tsx} (98%) delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/helpers.js create mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/helpers.ts rename packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/{index.js => index.ts} (100%) delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-card/index.js create mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-card/index.ts rename packages/cashier/src/pages/payment-agent/payment-agent-card/{payment-agent-card-description.jsx => payment-agent-card-description.tsx} (91%) rename packages/cashier/src/pages/payment-agent/payment-agent-card/{payment-agent-card.jsx => payment-agent-card.tsx} (77%) rename packages/cashier/src/pages/payment-agent/payment-agent-container/__tests__/{payment-agent-container.spec.js => payment-agent-container.spec.tsx} (88%) delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-container/index.js create mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-container/index.ts rename packages/cashier/src/pages/payment-agent/payment-agent-container/{payment-agent-container.jsx => payment-agent-container.tsx} (91%) rename packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/__tests__/{payment-agent-card-deposit-details.spec.js => payment-agent-card-deposit-details.spec.tsx} (84%) rename packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/{index.js => index.ts} (88%) rename packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/{payment-agent-deposit-details.jsx => payment-agent-deposit-details.tsx} (82%) rename packages/cashier/src/pages/payment-agent/payment-agent-detail/__tests__/{payment-agent-detail.spec.js => payment-agent-detail.spec.tsx} (94%) delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-detail/index.js create mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-detail/index.ts rename packages/cashier/src/pages/payment-agent/payment-agent-detail/{payment-agent-detail.jsx => payment-agent-detail.tsx} (76%) delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-details/payment-agent-details.scss delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/index.js create mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/index.ts rename packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/{payment-agent-disclaimer.jsx => payment-agent-disclaimer.tsx} (90%) rename packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/__tests__/{payment-agent-listed-withdraw-form.spec.js => payment-agent-listed-withdraw-form.spec.tsx} (79%) rename packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/{index.js => index.ts} (82%) rename packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/{payment-agent-listed-withdraw-form.jsx => payment-agent-listed-withdraw-form.tsx} (80%) rename packages/cashier/src/pages/payment-agent/payment-agent-receipt/__tests__/{payment-agent-receipt.spec.js => payment-agent-receipt.spec.tsx} (92%) delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-receipt/index.js create mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-receipt/index.ts rename packages/cashier/src/pages/payment-agent/payment-agent-receipt/{payment-agent-receipt.jsx => payment-agent-receipt.tsx} (82%) delete mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-search-box/index.js create mode 100644 packages/cashier/src/pages/payment-agent/payment-agent-search-box/index.ts rename packages/cashier/src/pages/payment-agent/payment-agent-search-box/{payment-agent-search-box.jsx => payment-agent-search-box.tsx} (96%) rename packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/__tests__/{payment-agent-withdraw-form.spec.js => payment-agent-withdraw-form.spec.tsx} (78%) rename packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/{index.js => index.ts} (80%) rename packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/{payment-agent-unlisted-withdraw-form.jsx => payment-agent-unlisted-withdraw-form.tsx} (81%) rename packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/__tests__/{payment-agent-withdraw-confirm.spec.js => payment-agent-withdraw-confirm.spec.tsx} (87%) rename packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/{index.js => index.ts} (87%) rename packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/{payment-agent-withdraw-confirm.jsx => payment-agent-withdraw-confirm.tsx} (78%) rename packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/{index.js => index.ts} (85%) rename packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/{payment-agent-withdrawal-locked.jsx => payment-agent-withdrawal-locked.tsx} (78%) rename packages/cashier/src/pages/payment-agent/{payment-agent.jsx => payment-agent.tsx} (90%) rename packages/cashier/src/stores/__tests__/{payment-agent-store.spec.js => payment-agent-store.spec.tsx} (65%) rename packages/cashier/src/stores/__tests__/{payment-agent-transfer-store.spec.js => payment-agent-transfer-store.spec.ts} (72%) rename packages/cashier/src/stores/{payment-agent-store.js => payment-agent-store.ts} (73%) rename packages/cashier/src/stores/{payment-agent-transfer-store.js => payment-agent-transfer-store.ts} (70%) create mode 100644 packages/cashier/src/types/payment-agent.types.ts rename packages/cashier/src/utils/{server_time.js => server_time.ts} (74%) delete mode 100644 packages/cashier/src/utils/utility.js create mode 100644 packages/cashier/src/utils/utility.ts delete mode 100644 packages/cashier/src/utils/validator/index.js delete mode 100644 packages/cashier/src/utils/validator/validator.js create mode 100644 packages/shared/src/utils/validator/__tests__/error.spec.tsx rename packages/{cashier/src/utils/validator/errors.js => shared/src/utils/validator/errors.ts} (79%) create mode 100644 packages/shared/src/utils/validator/index.ts create mode 100644 packages/shared/src/utils/validator/validator.ts diff --git a/packages/appstore/src/components/routes/routes-wrapper.tsx b/packages/appstore/src/components/routes/routes-wrapper.tsx index 4f2803ff312b..8092cd5b2913 100644 --- a/packages/appstore/src/components/routes/routes-wrapper.tsx +++ b/packages/appstore/src/components/routes/routes-wrapper.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; -import { TReactChildren } from 'Types'; const RoutesWrapper: React.FC = ({ has_router, children }) => { if (has_router) { @@ -10,9 +9,8 @@ const RoutesWrapper: React.FC = ({ has_router, children }) return {children}; }; -type TRoutesWrapperProps = { +type TRoutesWrapperProps = React.PropsWithChildren<{ has_router: boolean; - children: TReactChildren; -}; +}>; export default RoutesWrapper; diff --git a/packages/appstore/src/types/props.types.ts b/packages/appstore/src/types/props.types.ts index 5d991bfa4be0..ed94bb03b11d 100644 --- a/packages/appstore/src/types/props.types.ts +++ b/packages/appstore/src/types/props.types.ts @@ -4,19 +4,3 @@ export type TConfigProps = { has_router: boolean; routes: ConfigStore['routes']; }; - -type ReactTypes = React.ComponentType | React.ElementType; - -type TLocalizeProps = { - components?: ReactTypes[]; - i18n?: unknown; - i18n_default_text: string; - values?: { - [k: string]: string; - }; -}; - -export type TStringTranslation = string | React.ReactElement; - -// ref: https://www.carlrippon.com/react-children-with-typescript/ -export type TReactChildren = React.ReactNode; diff --git a/packages/cashier/src/components/account-platform-icon/account-platform-icon.tsx b/packages/cashier/src/components/account-platform-icon/account-platform-icon.tsx index b0a5679ffef2..0cc4e54db2e9 100644 --- a/packages/cashier/src/components/account-platform-icon/account-platform-icon.tsx +++ b/packages/cashier/src/components/account-platform-icon/account-platform-icon.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Icon } from '@deriv/components'; import TradingPlatformIcon from 'Assets/svgs/trading-platform'; -import { TAccountsList } from 'Types'; +import { TAccountsList } from '../../types'; type TAccountPlatformIcon = { size: number; diff --git a/packages/cashier/src/components/account-prompt-dialog/__tests__/account-prompt-dialog.spec.tsx b/packages/cashier/src/components/account-prompt-dialog/__tests__/account-prompt-dialog.spec.tsx index f2de713b962f..ff1ecdc3bb71 100644 --- a/packages/cashier/src/components/account-prompt-dialog/__tests__/account-prompt-dialog.spec.tsx +++ b/packages/cashier/src/components/account-prompt-dialog/__tests__/account-prompt-dialog.spec.tsx @@ -10,7 +10,7 @@ jest.mock('@deriv/components', () => ({ })); describe('', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { accounts: { CR90000001: { is_virtual: 0, currency: 'USD' }, @@ -32,7 +32,7 @@ describe('', () => { }); it('should render dialog', () => { render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Dialog')).toBeInTheDocument(); diff --git a/packages/cashier/src/components/cashier-container/real/__tests__/real.spec.tsx b/packages/cashier/src/components/cashier-container/real/__tests__/real.spec.tsx index c26ecdbb29c1..30722a002d50 100644 --- a/packages/cashier/src/components/cashier-container/real/__tests__/real.spec.tsx +++ b/packages/cashier/src/components/cashier-container/real/__tests__/real.spec.tsx @@ -35,7 +35,7 @@ describe('', () => { }; }); - const mockRootStore = mockStore({}); + const mock_root_store = mockStore({}); it('should show loader when is_loading is true or iframe_height is equal to 0', () => { (useCashierStore as jest.Mock).mockReturnValueOnce({ @@ -44,7 +44,7 @@ describe('', () => { }); const { rerender } = render( - + ); @@ -57,7 +57,7 @@ describe('', () => { }); rerender( - + ); @@ -67,7 +67,7 @@ describe('', () => { it('should render an iframe if iframe_url is not an empty string', () => { render( - + ); @@ -78,7 +78,7 @@ describe('', () => { describe('Breadcrumb visibility', () => { it('should show breadcrumbs only on deposit page and only for non EU clients', () => { render( - + ); @@ -94,7 +94,7 @@ describe('', () => { }); render( - + ); @@ -105,7 +105,7 @@ describe('', () => { it('should not show breadcrumbs on withdraw page', () => { render( - + ); @@ -117,7 +117,7 @@ describe('', () => { it('should trigger setIsDeposit callback when the user clicks on Cashier breadcrumb', () => { render( - + ); @@ -132,7 +132,7 @@ describe('', () => { it('should trigger clearIframe and onMountDeposit callbacks when component is destroyed on deposit page', () => { const { unmount } = render( - + ); diff --git a/packages/cashier/src/components/cashier-onboarding/__tests__/cashier-onboarding-side-note.spec.tsx b/packages/cashier/src/components/cashier-onboarding/__tests__/cashier-onboarding-side-note.spec.tsx index 5b14e1bb40a0..a86fb9b09ca1 100644 --- a/packages/cashier/src/components/cashier-onboarding/__tests__/cashier-onboarding-side-note.spec.tsx +++ b/packages/cashier/src/components/cashier-onboarding/__tests__/cashier-onboarding-side-note.spec.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import CashierOnboardingSideNote from '../cashier-onboarding-side-note'; -import type { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../cashier-providers'; describe('', () => { - let mockRootStore: DeepPartial; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { currency: 'USD', }, @@ -21,7 +21,7 @@ describe('', () => { }, }, }, - }; + }); }); const props = { @@ -45,6 +45,7 @@ describe('', () => { it('should trigger onClick callback when the client clicks the "live chat" link', () => { window.LC_API = { + on_chat_ended: jest.fn(), open_chat_window: jest.fn(), }; renderCashierOnboardingSideNote(); diff --git a/packages/cashier/src/components/cashier-onboarding/__tests__/cashier-onboarding.spec.tsx b/packages/cashier/src/components/cashier-onboarding/__tests__/cashier-onboarding.spec.tsx index 79be6ea10076..6bdf53a8a90c 100644 --- a/packages/cashier/src/components/cashier-onboarding/__tests__/cashier-onboarding.spec.tsx +++ b/packages/cashier/src/components/cashier-onboarding/__tests__/cashier-onboarding.spec.tsx @@ -4,7 +4,7 @@ import { fireEvent, render, screen } from '@testing-library/react'; import CashierOnboarding from '../cashier-onboarding'; import { Router } from 'react-router'; import { routes } from '@deriv/shared'; -import type { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../cashier-providers'; jest.mock('@deriv/hooks', () => { @@ -17,9 +17,9 @@ jest.mock('@deriv/hooks', () => { }); describe('', () => { - let mockRootStore: DeepPartial; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { accounts: { CR90000001: { is_virtual: 0, currency: 'USD' } }, account_status: { @@ -64,7 +64,7 @@ describe('', () => { }, }, }, - }; + }); }); const props = () => ({ @@ -279,7 +279,7 @@ describe('', () => { const node_list = screen.getAllByTestId('dt_cashier_onboarding_detail_div'); const deposit_with_dp2p_detail_div = Array.from(node_list).find(node => - node.textContent.includes( + node.textContent?.includes( 'Deposit with your local currency via peer-to-peer exchange with fellow traders in your country.' ) ); @@ -302,7 +302,7 @@ describe('', () => { const node_list = screen.getAllByTestId('dt_cashier_onboarding_detail_div'); const deposit_with_dp2p_detail_div = Array.from(node_list).find(node => - node.textContent.includes( + node.textContent?.includes( 'Deposit with your local currency via peer-to-peer exchange with fellow traders in your country.' ) ); @@ -322,7 +322,7 @@ describe('', () => { const node_list = screen.getAllByTestId('dt_cashier_onboarding_detail_div'); const deposit_with_dp2p_detail_div = Array.from(node_list).find(node => - node.textContent.includes( + node.textContent?.includes( 'Deposit with your local currency via peer-to-peer exchange with fellow traders in your country.' ) ); diff --git a/packages/cashier/src/components/cashier-search-box/__tests__/cashier-search-box.spec.js b/packages/cashier/src/components/cashier-search-box/__tests__/cashier-search-box.spec.tsx similarity index 98% rename from packages/cashier/src/components/cashier-search-box/__tests__/cashier-search-box.spec.js rename to packages/cashier/src/components/cashier-search-box/__tests__/cashier-search-box.spec.tsx index cb912390b92f..c9488473c5e0 100644 --- a/packages/cashier/src/components/cashier-search-box/__tests__/cashier-search-box.spec.js +++ b/packages/cashier/src/components/cashier-search-box/__tests__/cashier-search-box.spec.tsx @@ -4,6 +4,8 @@ import CashierSearchBox from '../cashier-search-box'; describe('', () => { const props = { + className: '', + search_term: '', onClear: jest.fn(), onSearch: jest.fn(), placeholder: 'Search placeholder', diff --git a/packages/cashier/src/components/cashier-search-box/cashier-search-box.jsx b/packages/cashier/src/components/cashier-search-box/cashier-search-box.tsx similarity index 71% rename from packages/cashier/src/components/cashier-search-box/cashier-search-box.jsx rename to packages/cashier/src/components/cashier-search-box/cashier-search-box.tsx index a2b16f25dda4..cf9809b1a6da 100644 --- a/packages/cashier/src/components/cashier-search-box/cashier-search-box.jsx +++ b/packages/cashier/src/components/cashier-search-box/cashier-search-box.tsx @@ -1,22 +1,37 @@ import classNames from 'classnames'; -import PropTypes from 'prop-types'; import React from 'react'; -import { Field as FormField, Formik, Form } from 'formik'; +import { Field as FormField, Formik, Form, FieldProps } from 'formik'; import { Icon, Input } from '@deriv/components'; import './cashier-search-box.scss'; -const CashierSearchBox = ({ className, onClear, onSearch, placeholder, search_term, setIsSearchLoading }) => { +type TCashierSearchBoxProps = { + className?: string; + onClear: VoidFunction; + onSearch: (search: string) => void; + placeholder: string; + search_term: string; + setIsSearchLoading: (value: boolean) => void; +}; + +const CashierSearchBox = ({ + className, + onClear, + onSearch, + placeholder, + search_term, + setIsSearchLoading, +}: TCashierSearchBoxProps) => { React.useEffect(() => { return onClear; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const onSearchClear = setFieldValue => { + const onSearchClear = (setFieldValue: (field: string, value: string) => void) => { setFieldValue('search', ''); onClear(); }; - const onSearchKeyUpDown = (submitForm, search) => { + const onSearchKeyUpDown = (submitForm: VoidFunction, search: string) => { if (!search.trim() && search_term) { onClear(); } else if (!search.trim()) return; @@ -25,7 +40,7 @@ const CashierSearchBox = ({ className, onClear, onSearch, placeholder, search_te submitForm(); }; - const onSearchSubmit = ({ search }) => { + const onSearchSubmit = ({ search }: { search: string }) => { onSearch(search); }; @@ -35,7 +50,7 @@ const CashierSearchBox = ({ className, onClear, onSearch, placeholder, search_te {({ submitForm, values: { search }, setFieldValue }) => (
- {({ field }) => ( + {({ field }: FieldProps) => ( onSearchKeyUpDown(submitForm, search)} leading_icon={} trailing_icon={ - search && ( + search ? ( onSearchClear(setFieldValue)} /> - ) + ) : null } /> )} @@ -64,13 +79,4 @@ const CashierSearchBox = ({ className, onClear, onSearch, placeholder, search_te ); }; -CashierSearchBox.propTypes = { - className: PropTypes.string, - onClear: PropTypes.func, - onSearch: PropTypes.func, - placeholder: PropTypes.string, - search_term: PropTypes.string, - setIsSearchLoading: PropTypes.func, -}; - export default CashierSearchBox; diff --git a/packages/cashier/src/components/cashier-search-box/index.js b/packages/cashier/src/components/cashier-search-box/index.js deleted file mode 100644 index 0be63590e6ce..000000000000 --- a/packages/cashier/src/components/cashier-search-box/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import CashierSearchBox from './cashier-search-box.jsx'; - -export default CashierSearchBox; diff --git a/packages/cashier/src/components/cashier-search-box/index.ts b/packages/cashier/src/components/cashier-search-box/index.ts new file mode 100644 index 000000000000..4d3137891e44 --- /dev/null +++ b/packages/cashier/src/components/cashier-search-box/index.ts @@ -0,0 +1,3 @@ +import CashierSearchBox from './cashier-search-box'; + +export default CashierSearchBox; diff --git a/packages/cashier/src/components/crypto-fiat-converter/__tests__/crypto-fiat-converter.spec.tsx b/packages/cashier/src/components/crypto-fiat-converter/__tests__/crypto-fiat-converter.spec.tsx index 79ce1ba999a6..b5443550eb5d 100644 --- a/packages/cashier/src/components/crypto-fiat-converter/__tests__/crypto-fiat-converter.spec.tsx +++ b/packages/cashier/src/components/crypto-fiat-converter/__tests__/crypto-fiat-converter.spec.tsx @@ -4,12 +4,13 @@ import CryptoFiatConverter from '../crypto-fiat-converter'; import { Formik } from 'formik'; import * as formik from 'formik'; import CashierProviders from '../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let mockRootStore, mockProps; + let mockRootStore: ReturnType, mockProps: React.ComponentProps; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ modules: { cashier: { crypto_fiat_converter: { @@ -19,23 +20,24 @@ describe('', () => { }, }, }, - }; + }); mockProps = { from_currency: 'BTC', hint: 'Transfer limits', - is_timer_visible: true, resetConverter: jest.fn(), to_currency: 'USD', onChangeConverterFromAmount: jest.fn(), onChangeConverterToAmount: jest.fn(), + validateFromAmount: jest.fn(), + validateToAmount: jest.fn(), }; }); const renderCryptoFiatConverter = () => { return render( - + Promise.resolve()}> diff --git a/packages/cashier/src/components/crypto-fiat-converter/crypto-fiat-converter.tsx b/packages/cashier/src/components/crypto-fiat-converter/crypto-fiat-converter.tsx index 8db13e1258fc..11e057b0c066 100644 --- a/packages/cashier/src/components/crypto-fiat-converter/crypto-fiat-converter.tsx +++ b/packages/cashier/src/components/crypto-fiat-converter/crypto-fiat-converter.tsx @@ -4,7 +4,7 @@ import { DesktopWrapper, Input, Icon, MobileWrapper, Text, useInterval } from '@ import { getCurrencyDisplayCode } from '@deriv/shared'; import { localize, Localize } from '@deriv/translations'; import { observer } from '@deriv/stores'; -import { TReactChangeEvent, TReactChildren } from '../../types'; +import { TReactChangeEvent } from '../../types'; import { useCashierStore } from '../../stores/useCashierStores'; import './crypto-fiat-converter.scss'; @@ -12,14 +12,13 @@ type TTimerProps = { onComplete: VoidFunction; }; -type TInputGroupProps = { - children: TReactChildren; +type TInputGroupProps = React.PropsWithChildren<{ className: string; -}; +}>; type TCryptoFiatConverterProps = { from_currency: string; - hint?: string | TReactChildren; + hint?: React.ReactNode; onChangeConverterFromAmount: ( event: { target: { value: string } }, from_currency: string, diff --git a/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-cancel-modal.spec.tsx b/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-cancel-modal.spec.tsx index a01fa2776a1a..ba210dad435a 100644 --- a/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-cancel-modal.spec.tsx +++ b/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-cancel-modal.spec.tsx @@ -2,11 +2,12 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import CryptoTransactionsCancelModal from '../crypto-transactions-cancel-modal'; import CashierProviders from '../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let modal_root_el, mockRootStore; + let modal_root_el: HTMLDivElement, mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ modules: { cashier: { transaction_history: { @@ -16,7 +17,7 @@ describe('', () => { }, }, }, - }; + }); }); beforeAll(() => { @@ -61,7 +62,6 @@ describe('', () => { renderCryptoTransactionsCancelModal(); - const yes_btn = screen.getByText('Yes'); const no_btn = screen.getByText('No'); fireEvent.click(no_btn); diff --git a/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-history.spec.tsx b/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-history.spec.tsx index 5f75b7b3fd40..cbed2ae61ab4 100644 --- a/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-history.spec.tsx +++ b/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-history.spec.tsx @@ -2,12 +2,13 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import CryptoTransactionsHistory from '../crypto-transactions-history'; import CashierProviders from '../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ modules: { cashier: { transaction_history: { @@ -23,7 +24,7 @@ describe('', () => { client: { currency: 'BTC', }, - }; + }); }); const renderCryptoTransactionsHistory = () => diff --git a/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-renderer.spec.tsx b/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-renderer.spec.tsx index fb9882e9f5af..8d0f6ac87d61 100644 --- a/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-renderer.spec.tsx +++ b/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-renderer.spec.tsx @@ -4,6 +4,7 @@ import userEvent from '@testing-library/user-event'; import { isMobile } from '@deriv/shared'; import CryptoTransactionsRenderer from '../crypto-transactions-renderer'; import CashierProviders from '../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; jest.mock('@deriv/shared/src/utils/screen/responsive', () => ({ ...jest.requireActual('@deriv/shared/src/utils/screen/responsive'), @@ -11,9 +12,9 @@ jest.mock('@deriv/shared/src/utils/screen/responsive', () => ({ })); describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ modules: { cashier: { transaction_history: { @@ -26,7 +27,7 @@ describe('', () => { client: { currency: 'BTC', }, - }; + }); }); const props = { row: { @@ -39,9 +40,12 @@ describe('', () => { status_message: "We're reviewing your withdrawal request. You may still cancel this transaction if you wish. Once we start processing, you won't be able to cancel.", submit_date: 1640603927, + transaction_hash: '', transaction_type: 'withdrawal', + transaction_url: + 'https://etherscan.io/tx/0x2aede798a325c96784c62073a5bd5e104a983fb47291a2d45992b40da636051e', }, - }; + } as const; const renderCryptoTransactionsRenderer = () => render(, { diff --git a/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-status-modal.spec.tsx b/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-status-modal.spec.tsx index 9c1809a4b7a2..c27bf90df535 100644 --- a/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-status-modal.spec.tsx +++ b/packages/cashier/src/components/crypto-transactions-history/__tests__/crypto-transactions-status-modal.spec.tsx @@ -2,11 +2,12 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import CryptoTransactionsStatusModal from '../crypto-transactions-status-modal'; import CashierProviders from '../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let modal_root_el, mockRootStore; + let modal_root_el: HTMLDivElement, mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ modules: { cashier: { transaction_history: { @@ -17,7 +18,7 @@ describe('', () => { }, }, }, - }; + }); }); beforeAll(() => { modal_root_el = document.createElement('div'); diff --git a/packages/cashier/src/components/crypto-transactions-history/crypto-transactions-renderer.tsx b/packages/cashier/src/components/crypto-transactions-history/crypto-transactions-renderer.tsx index 00770b9e8a8c..fcd286e43d50 100644 --- a/packages/cashier/src/components/crypto-transactions-history/crypto-transactions-renderer.tsx +++ b/packages/cashier/src/components/crypto-transactions-history/crypto-transactions-renderer.tsx @@ -57,14 +57,12 @@ const CryptoTransactionsRenderer = observer(({ row: crypto, onTooltipClick }: TC showCryptoTransactionsCancelModal(id); }; const onClickStatus = () => { - const description = status.description; - const name = status.name; - showCryptoTransactionsStatusModal(description, name); + if (status) showCryptoTransactionsStatusModal(status.description, status.name); }; const is_third_party_transaction = transaction_url?.includes('CP:'); - if (isMobile()) { + if (status && isMobile()) { return (
@@ -201,19 +199,21 @@ const CryptoTransactionsRenderer = observer(({ row: crypto, onTooltipClick }: TC - - - + {status && ( + + + + )} - {transaction_url ? ( - <> - - - - {status.transaction_hash} - - - - {is_third_party_transaction && ( + {status && + (transaction_url ? ( + <> - + + + {status.transaction_hash} + + - )} - - ) : ( - - {status.transaction_hash} - - )} + {is_third_party_transaction && ( + + + + )} + + ) : ( + + {status.transaction_hash} + + ))} {!is_transaction_clicked && ( @@ -278,21 +279,23 @@ const CryptoTransactionsRenderer = observer(({ row: crypto, onTooltipClick }: TC )} {!is_transaction_clicked && ( - -
- - {status.name} - - + {status && ( + +
+ + {status.name} + + + )} )} {is_transaction_clicked ? ( diff --git a/packages/cashier/src/components/email-verification-empty-state/__tests__/email-verification-empty-state.test.tsx b/packages/cashier/src/components/email-verification-empty-state/__tests__/email-verification-empty-state.test.tsx index 87a157bc470a..5134d5ad81fb 100644 --- a/packages/cashier/src/components/email-verification-empty-state/__tests__/email-verification-empty-state.test.tsx +++ b/packages/cashier/src/components/email-verification-empty-state/__tests__/email-verification-empty-state.test.tsx @@ -1,19 +1,19 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import EmailVerificationEmptyState from '../email-verification-empty-state'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../cashier-providers'; -const mock_store: DeepPartial = { +const mock_store = mockStore({ client: { email: 'john@company.com', }, -}; +}); describe('EmailVerificationEmptyState', () => { test('should disable resend button after sending the request', () => { render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.queryByTestId('dt_empty_state_action')).toBeInTheDocument(); diff --git a/packages/cashier/src/components/email-verification-empty-state/email-verification-empty-state.tsx b/packages/cashier/src/components/email-verification-empty-state/email-verification-empty-state.tsx index 479417cee08a..2ee4392b5192 100644 --- a/packages/cashier/src/components/email-verification-empty-state/email-verification-empty-state.tsx +++ b/packages/cashier/src/components/email-verification-empty-state/email-verification-empty-state.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { useVerifyEmail, TEmailVerificationType } from '@deriv/hooks'; +import { useVerifyEmail } from '@deriv/hooks'; import { localize } from '@deriv/translations'; import EmptyState from 'Components/empty-state'; import EmailVerificationResendEmptyState from './email-verification-resend-empty-state'; import './email-verification-empty-state.scss'; type TEmailVerificationEmptyStateProps = { - type: TEmailVerificationType; + type: Parameters[0]; }; const EmailVerificationEmptyState = ({ type }: TEmailVerificationEmptyStateProps) => { diff --git a/packages/cashier/src/components/empty-state/empty-state.tsx b/packages/cashier/src/components/empty-state/empty-state.tsx index f2afa8436330..bec6a5862c88 100644 --- a/packages/cashier/src/components/empty-state/empty-state.tsx +++ b/packages/cashier/src/components/empty-state/empty-state.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { Icon, Text, Button } from '@deriv/components'; -import { TReactChildren } from 'Types'; import './empty-state.scss'; type TAction = { @@ -13,10 +12,10 @@ type TAction = { export type TEmptyStateProps = { icon?: string; - title?: string | TReactChildren; - description?: string | TReactChildren; + title?: React.ReactNode; + description?: React.ReactNode; action?: TAction; -} & ({ title: string | TReactChildren } | { description: string | TReactChildren }); +} & ({ title: React.ReactNode } | { description: React.ReactNode }); const EmptyState = ({ icon, title, description, action }: TEmptyStateProps) => (
diff --git a/packages/cashier/src/components/error-dialog/__tests__/error-dialog.spec.tsx b/packages/cashier/src/components/error-dialog/__tests__/error-dialog.spec.tsx index fb66fa5d8eca..3fa1e4d4b83e 100644 --- a/packages/cashier/src/components/error-dialog/__tests__/error-dialog.spec.tsx +++ b/packages/cashier/src/components/error-dialog/__tests__/error-dialog.spec.tsx @@ -4,15 +4,15 @@ import { createBrowserHistory } from 'history'; import { Router } from 'react-router'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { routes } from '@deriv/shared'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../cashier-providers'; -const mockRootStore: DeepPartial = { +const mock_root_store = mockStore({ ui: { disableApp: jest.fn(), enableApp: jest.fn() }, -}; +}); describe('', () => { - let modal_root_el; + let modal_root_el: HTMLDivElement; beforeAll(() => { modal_root_el = document.createElement('div'); modal_root_el.setAttribute('id', 'modal_root'); @@ -25,9 +25,7 @@ describe('', () => { it('should show "Please verify your identity" message, "Cancel" and "Verify identity" buttons', () => { render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Please verify your identity')).toBeInTheDocument(); @@ -37,20 +35,17 @@ describe('', () => { it('should redirect to "/account/proof-of-identity" page, when "Verify identity" button was clicked', () => { const history = createBrowserHistory(); - const setErrorMessage = jest.fn(); const error = { code: 'Fiat2CryptoTransferOverLimit', - message: 'Error is occured', - setErrorMessage, + message: 'Error has occurred', + setErrorMessage: jest.fn(), }; render( , { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, } ); const on_confirm_btn = screen.getByText('Verify identity'); @@ -60,10 +55,8 @@ describe('', () => { }); it('should show "Cashier Error" message and "OK" button', () => { - render(, { - wrapper: ({ children }) => ( - {children} - ), + render(, { + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Cashier Error')).toBeInTheDocument(); @@ -71,16 +64,13 @@ describe('', () => { }); it('should not show "Cashier Error" message, when "OK" button was clicked', async () => { - const setErrorMessage = jest.fn(); const error = { code: '', - message: 'Error is occured', - setErrorMessage, + message: 'Error has occurred', + setErrorMessage: jest.fn(), }; render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const ok_btn = screen.getByText('OK'); fireEvent.click(ok_btn); @@ -91,16 +81,13 @@ describe('', () => { }); it('should not show "Please verify your identity" message, when "Cancel" button was clicked', async () => { - const setErrorMessage = jest.fn(); const error = { code: 'Fiat2CryptoTransferOverLimit', - message: 'Error is occured', - setErrorMessage, + message: 'Error has occurred', + setErrorMessage: jest.fn(), }; render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const cancel_btn = screen.getByText('Cancel'); fireEvent.click(cancel_btn); @@ -111,12 +98,12 @@ describe('', () => { }); it('should clear an error.message if one of the buttons ["Verify identity", "Cancel", "OK"] was clicked', () => { - const checkButton = (error_code, btn_name) => { + const checkButton = (error_code: string, btn_name: string) => { const history = createBrowserHistory(); const error = { code: error_code, - message: 'Error is occured', - setErrorMessage({ code, message }) { + message: 'Error has occurred', + setErrorMessage({ code, message }: { code: string; message: string }) { this.message = message; }, }; @@ -125,9 +112,7 @@ describe('', () => { , { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, } ); const error_btn = screen.getByText(btn_name); diff --git a/packages/cashier/src/components/error-dialog/error-dialog.tsx b/packages/cashier/src/components/error-dialog/error-dialog.tsx index 99d1f8cdea22..58102d2fb5d0 100644 --- a/packages/cashier/src/components/error-dialog/error-dialog.tsx +++ b/packages/cashier/src/components/error-dialog/error-dialog.tsx @@ -8,7 +8,7 @@ import { TError, TReactElement } from '../../types'; type TErrorDialogProps = { className?: string; - error?: TError | Record; + error?: Partial | Record; }; type TSetDetails = { diff --git a/packages/cashier/src/components/error/__tests__/error.spec.tsx b/packages/cashier/src/components/error/__tests__/error.spec.tsx index 6bfe40cbcffa..d531855762dc 100644 --- a/packages/cashier/src/components/error/__tests__/error.spec.tsx +++ b/packages/cashier/src/components/error/__tests__/error.spec.tsx @@ -8,6 +8,10 @@ describe('', () => { it('should show the "Email verification failed" message, and "Resend email" button', () => { const error = { code: 'InvalidToken', + fields: '', + message: '', + onClickButton: jest.fn(), + setErrorMessage: jest.fn(), }; render(); expect(screen.getByText('Email verification failed')).toBeInTheDocument(); @@ -19,6 +23,10 @@ describe('', () => { const history = createBrowserHistory(); const error = { code: 'ASK_FIX_DETAILS', + fields: '', + message: '', + onClickButton: jest.fn(), + setErrorMessage: jest.fn(), }; render( @@ -34,6 +42,9 @@ describe('', () => { const error = { code: 'WrongResponse', message: 'Oops, you have an error!', + fields: '', + onClickButton: jest.fn(), + setErrorMessage: jest.fn(), }; render(); @@ -45,6 +56,9 @@ describe('', () => { const error = { code: 'PaymentAgentWithdrawError', message: 'Oops, you have an error with withdrawal!', + fields: '', + onClickButton: jest.fn(), + setErrorMessage: jest.fn(), }; render(); @@ -55,6 +69,9 @@ describe('', () => { const error = { code: '', message: 'Default error', + fields: '', + onClickButton: jest.fn(), + setErrorMessage: jest.fn(), }; render(); @@ -62,14 +79,16 @@ describe('', () => { }); it('should clear an error.message if one of the buttons ["Resend email", "Update my details", "Try again"] was clicked', () => { - const checkButton = (btn_name, error_code) => { + const checkButton = (btn_name: string, error_code: string) => { const history = createBrowserHistory(); const error = { code: error_code, - setErrorMessage({ code, message }) { + fields: '', + setErrorMessage({ code, message }: { code: string; message: string }) { this.message = message; }, message: '', + onClickButton: jest.fn(), }; const { unmount } = render( diff --git a/packages/cashier/src/components/error/error.tsx b/packages/cashier/src/components/error/error.tsx index e17d6b28cb65..b6ece1ef2bca 100644 --- a/packages/cashier/src/components/error/error.tsx +++ b/packages/cashier/src/components/error/error.tsx @@ -47,7 +47,12 @@ const ErrorComponent = ({ header, message, button_link, onClickButton, button_te
); -const Error = ({ error }: { error: ErrorStore }) => { +const Error = ({ + error, +}: { + error: Pick & + Partial>; +}) => { const error_fields: TErrorFields = { address_city: localize('Town/City'), address_line_1: localize('First line of home address'), diff --git a/packages/cashier/src/components/percentage-selector/percentage-selector.tsx b/packages/cashier/src/components/percentage-selector/percentage-selector.tsx index ba427bac3fe8..530c6a596ef9 100644 --- a/packages/cashier/src/components/percentage-selector/percentage-selector.tsx +++ b/packages/cashier/src/components/percentage-selector/percentage-selector.tsx @@ -59,7 +59,7 @@ const PercentageSelector = ({ if (percentage_selector_block) { if ( i < (e as TCalculateAmountInputEvent).target.id || - (i === +(e as TCalculateAmountInputEvent).target.id && !is_percentage_selected) + (i === Number((e as TCalculateAmountInputEvent).target.id) && !is_percentage_selected) ) { percentage_selector_block.style.backgroundColor = 'var(--status-success)'; } else { diff --git a/packages/cashier/src/components/read-more-wrapper/read-more-wrapper.tsx b/packages/cashier/src/components/read-more-wrapper/read-more-wrapper.tsx index 83dfcf93fce1..313f1280b8cc 100644 --- a/packages/cashier/src/components/read-more-wrapper/read-more-wrapper.tsx +++ b/packages/cashier/src/components/read-more-wrapper/read-more-wrapper.tsx @@ -1,7 +1,7 @@ import { ReadMore } from '@deriv/components'; import React from 'react'; import { localize } from '@deriv/translations'; -import { TReactElement } from 'Types'; +import { TReactElement } from '../../types'; type TReadMoreWrapperProps = { error_content: string | TReactElement; diff --git a/packages/cashier/src/components/recent-transaction/__tests__/recent-transaction.spec.tsx b/packages/cashier/src/components/recent-transaction/__tests__/recent-transaction.spec.tsx index c8ca30dfc777..6c5ee13fff45 100644 --- a/packages/cashier/src/components/recent-transaction/__tests__/recent-transaction.spec.tsx +++ b/packages/cashier/src/components/recent-transaction/__tests__/recent-transaction.spec.tsx @@ -1,15 +1,16 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import RecentTransaction from '../recent-transaction'; -import { createBrowserHistory } from 'history'; +import { BrowserHistory, createBrowserHistory } from 'history'; import { Router } from 'react-router'; import CashierProviders from '../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let history, mockRootStore; + let history: BrowserHistory, mockRootStore: ReturnType; beforeEach(() => { history = createBrowserHistory(); - mockRootStore = { + mockRootStore = mockStore({ client: { currency: 'BTC', }, @@ -36,7 +37,7 @@ describe('', () => { }, }, }, - }; + }); }); const renderRecentTransaction = () => { diff --git a/packages/cashier/src/components/recent-transaction/recent-transaction.tsx b/packages/cashier/src/components/recent-transaction/recent-transaction.tsx index 9b6277fb0799..0002e30de056 100644 --- a/packages/cashier/src/components/recent-transaction/recent-transaction.tsx +++ b/packages/cashier/src/components/recent-transaction/recent-transaction.tsx @@ -21,12 +21,13 @@ const RecentTransaction = observer(() => { if (!crypto_transactions.length) { return null; } - let { address_hash, submit_date, transaction_type } = crypto_transactions[0]; - const { status_code, transaction_hash } = crypto_transactions[0]; + const { address_hash, transaction_hash, transaction_type, status_code, submit_date } = crypto_transactions[0]; const status = getStatus(transaction_hash, transaction_type, status_code); - submit_date = epochToMoment(Number(submit_date)).format('MMM D, YYYY'); - transaction_type = transaction_type[0].toUpperCase() + transaction_type.slice(1); - address_hash = `${address_hash.substring(0, 4)}....${address_hash.substring(address_hash.length - 4)}`; + const submit_date_moment = epochToMoment(submit_date).format('MMM D, YYYY'); + const transaction_type_display_text = transaction_type[0].toUpperCase() + transaction_type.slice(1); + const address_hash_display_value = `${address_hash.substring(0, 4)}....${address_hash.substring( + address_hash.length - 4 + )}`; const amount = crypto_transactions[0].amount; @@ -43,40 +44,42 @@ const RecentTransaction = observer(() => {
-
- - - {status.name} - -
+ {status && ( +
+ + + {status.name} + +
+ )}
@@ -87,7 +90,7 @@ const RecentTransaction = observer(() => {   - {address_hash} + {address_hash_display_value}
@@ -96,9 +99,11 @@ const RecentTransaction = observer(() => {   - - {status.transaction_hash} - + {status && ( + + {status.transaction_hash} + + )}
diff --git a/packages/cashier/src/components/side-note/side-note.tsx b/packages/cashier/src/components/side-note/side-note.tsx index f929f01ee4d8..3a310bd7ba7f 100644 --- a/packages/cashier/src/components/side-note/side-note.tsx +++ b/packages/cashier/src/components/side-note/side-note.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { DesktopWrapper, MobileWrapper, Text } from '@deriv/components'; import { Localize } from '@deriv/translations'; import { isMobile } from '@deriv/shared'; -import { TReactChildren, TSideNotesProps } from '../../types'; +import { TSideNotesProps } from '../../types'; import './side-note.scss'; type TSideNoteTitle = { @@ -12,10 +12,9 @@ type TSideNoteTitle = { title?: string | JSX.Element; }; -type TSideNoteBullet = { - children: TReactChildren; +type TSideNoteBullet = React.PropsWithChildren<{ id: number; -}; +}>; type TSideNoteProps = React.PropsWithChildren<{ className?: string; diff --git a/packages/cashier/src/components/transfer-confirm/__tests__/transfer-confirm.spec.tsx b/packages/cashier/src/components/transfer-confirm/__tests__/transfer-confirm.spec.tsx index b7f718707365..091d8e64514a 100644 --- a/packages/cashier/src/components/transfer-confirm/__tests__/transfer-confirm.spec.tsx +++ b/packages/cashier/src/components/transfer-confirm/__tests__/transfer-confirm.spec.tsx @@ -1,18 +1,19 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import TransferConfirm from '../transfer-confirm'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../cashier-providers'; +import { TError } from 'Types'; -const mockRootStore: DeepPartial = { +const mock_root_store = mockStore({ ui: { disableApp: jest.fn(), enableApp: jest.fn(), }, -}; +}); describe('', () => { - let modal_root_el; + let modal_root_el: HTMLDivElement; beforeAll(() => { modal_root_el = document.createElement('div'); modal_root_el.setAttribute('id', 'modal_root'); @@ -35,9 +36,7 @@ describe('', () => { it('should show proper icon, messages and buttons', () => { render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const [back_btn, transfer_now_btn] = screen.getAllByRole('button'); @@ -65,15 +64,15 @@ describe('', () => { render( , { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, } ); @@ -84,9 +83,7 @@ describe('', () => { it('should trigger onClickBack method when the client clicks on Back button', () => { render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const [back_btn, _] = screen.getAllByRole('button'); @@ -97,9 +94,7 @@ describe('', () => { it('should enable Transfer now button when checkbox is checked', () => { render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const el_checkbox = screen.getByRole('checkbox'); @@ -110,9 +105,7 @@ describe('', () => { }); it('should show proer checkbox label text when is_payment_agent_withdraw property is equal to true/false', () => { const { rerender } = render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect( @@ -128,9 +121,7 @@ describe('', () => { it('should trigger onClickConfirm method when the client clicks on Transfer now button', () => { render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const el_checkbox = screen.getByRole('checkbox'); diff --git a/packages/cashier/src/components/transfer-confirm/transfer-confirm.tsx b/packages/cashier/src/components/transfer-confirm/transfer-confirm.tsx index fb6d76677186..4101f46215e7 100644 --- a/packages/cashier/src/components/transfer-confirm/transfer-confirm.tsx +++ b/packages/cashier/src/components/transfer-confirm/transfer-confirm.tsx @@ -9,8 +9,8 @@ import './transfer-confirm.scss'; type TRowProps = { item_key?: string | number; - label: string | Array; - value: string | Array | JSX.Element; + label: string | string[]; + value: string | string[] | JSX.Element; key: string | number; }; @@ -19,13 +19,13 @@ type WarningBulletProps = { }; type TTransferConfirmProps = { - data: Array; + data: TRowProps[]; error?: TError | Record; header?: string; is_payment_agent_withdraw?: boolean; onClickBack?: VoidFunction; onClickConfirm?: VoidFunction; - warning_messages?: Array; + warning_messages?: JSX.Element[]; }; const Row = ({ item_key, label, value }: TRowProps) => ( diff --git a/packages/cashier/src/constants/constants.js b/packages/cashier/src/constants/constants.ts similarity index 99% rename from packages/cashier/src/constants/constants.js rename to packages/cashier/src/constants/constants.ts index 1e809a460091..a9d2fde7636f 100644 --- a/packages/cashier/src/constants/constants.js +++ b/packages/cashier/src/constants/constants.ts @@ -4,12 +4,12 @@ const containers = { payment_agent: 'payment_agent', payment_agent_transfer: 'payment_agent_transfer', withdraw: 'withdraw', -}; +} as const; const map_action = { withdraw: 'payment_withdraw', payment_agent: 'payment_agent_withdraw', -}; +} as const; const icon_payment_methods = { Alipay: ['alipay'], @@ -53,7 +53,7 @@ const icon_payment_methods = { WebMoney: ['perfectmoneyandwebmoney', 'webmoney'], Wechatpay: ['wechatpay'], Zenithbank: ['zenithbank'], -}; +} as const; const payment_methods = { AbokiFX: ['AbokiFX', 'A BOKI FX'], @@ -762,6 +762,6 @@ const payment_methods = { 'Zanaco bank': ['ZANACO', 'Zanaco bank'], 'Zenith bank': ['Zenith bank', 'Zenithbank', 'Zenith Bank', 'ZenithBank', 'ZENITH BANK', 'Zenith', 'zenithbank'], Zipit: ['Zipit', 'ZIPIT', 'ZIPIT bank transfers'], -}; +} as const; export default { containers, map_action, icon_payment_methods, payment_methods }; diff --git a/packages/cashier/src/constants/transaction-status.js b/packages/cashier/src/constants/transaction-status.tsx similarity index 78% rename from packages/cashier/src/constants/transaction-status.js rename to packages/cashier/src/constants/transaction-status.tsx index 492349d0c244..1ca0b3bd288a 100644 --- a/packages/cashier/src/constants/transaction-status.js +++ b/packages/cashier/src/constants/transaction-status.tsx @@ -1,21 +1,21 @@ import React from 'react'; import { StaticUrl } from '@deriv/components'; import { localize, Localize } from '@deriv/translations'; +import { TStatusCode, TTransactionType } from '../types'; -export const getStatus = (transaction_hash, transaction_type, status_code) => { - const formatted_status_code = status_code.toLowerCase(); +export const getStatus = (transaction_hash: string, transaction_type: TTransactionType, status_code: TStatusCode) => { const formatted_transaction_hash = transaction_hash ? `${transaction_hash.substring(0, 4)}....${transaction_hash.substring(transaction_hash.length - 4)}` : localize('Pending'); const status_list = { deposit: { - confirmed: { + CONFIRMED: { name: localize('Successful'), description: localize('Your deposit is successful.'), renderer: 'successful', transaction_hash: formatted_transaction_hash, }, - error: { + ERROR: { name: localize('Unsuccessful'), description: localize( 'Your deposit is unsuccessful due to an error on the blockchain. Please contact your crypto wallet service provider for more info.' @@ -23,7 +23,7 @@ export const getStatus = (transaction_hash, transaction_type, status_code) => { renderer: 'unsuccessful', transaction_hash: localize('NA'), }, - pending: { + PENDING: { name: localize('In process'), description: localize('We’ve received your request and are waiting for more blockchain confirmations.'), renderer: 'in-process', @@ -31,16 +31,17 @@ export const getStatus = (transaction_hash, transaction_type, status_code) => { }, }, withdrawal: { - cancelled: { + CANCELLED: { name: localize('Cancelled'), description: localize('You’ve cancelled your withdrawal request.'), renderer: 'unsuccessful', transaction_hash: localize('NA'), }, - error: { + ERROR: { name: localize('Unsuccessful'), description: ( { renderer: 'unsuccessful', transaction_hash: localize('NA'), }, - locked: { + LOCKED: { name: localize('In review'), description: localize( "We're reviewing your withdrawal request. You may still cancel this transaction if you wish. Once we start processing, you won't be able to cancel." @@ -59,19 +60,19 @@ export const getStatus = (transaction_hash, transaction_type, status_code) => { renderer: 'in-review', transaction_hash: formatted_transaction_hash, }, - performing_blockchain_txn: { + PERFORMING_BLOCKCHAIN_TXN: { name: localize('In process'), description: localize('We’re sending your request to the blockchain.'), renderer: 'in-process', transaction_hash: formatted_transaction_hash, }, - processing: { + PROCESSING: { name: localize('In process'), description: localize('We’re awaiting confirmation from the blockchain.'), renderer: 'in-process', transaction_hash: formatted_transaction_hash, }, - rejected: { + REJECTED: { name: localize('Unsuccessful'), description: localize( "Your withdrawal is unsuccessful. We've sent you an email with more information." @@ -79,13 +80,13 @@ export const getStatus = (transaction_hash, transaction_type, status_code) => { renderer: 'unsuccessful', transaction_hash: localize('NA'), }, - sent: { + SENT: { name: localize('Successful'), description: localize('Your withdrawal is successful.'), renderer: 'successful', transaction_hash: formatted_transaction_hash, }, - verified: { + VERIFIED: { name: localize('In process'), description: localize('We’re processing your withdrawal.'), renderer: 'in-process', @@ -94,5 +95,16 @@ export const getStatus = (transaction_hash, transaction_type, status_code) => { }, }; - return status_list[transaction_type][formatted_status_code]; + const isDeposit = (status: TStatusCode): status is keyof typeof status_list.deposit => + Object.keys(status_list.deposit).includes(status); + const isWithdrawal = (status: TStatusCode): status is keyof typeof status_list.withdrawal => + Object.keys(status_list.withdrawal).includes(status); + + if (transaction_type === 'deposit' && isDeposit(status_code)) { + return status_list[transaction_type][status_code]; + } else if (transaction_type === 'withdrawal' && isWithdrawal(status_code)) { + return status_list[transaction_type][status_code]; + } + + return null; }; diff --git a/packages/cashier/src/containers/cashier/__tests__/cashier.spec.tsx b/packages/cashier/src/containers/cashier/__tests__/cashier.spec.tsx index ace838c720b8..2ca61ef54d9a 100644 --- a/packages/cashier/src/containers/cashier/__tests__/cashier.spec.tsx +++ b/packages/cashier/src/containers/cashier/__tests__/cashier.spec.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; -import { createBrowserHistory } from 'history'; +import { BrowserHistory, createBrowserHistory } from 'history'; import { Router } from 'react-router'; import { isMobile } from '@deriv/shared'; import getRoutesConfig from 'Constants/routes-config'; import Cashier from '../cashier'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../cashier-providers'; jest.mock('@deriv/hooks', () => { @@ -36,7 +36,7 @@ jest.mock('@deriv/shared', () => { ...original_module, isMobile: jest.fn(() => false), WS: { - wait: (...payload) => { + wait: (...payload: unknown[]) => { return Promise.resolve([...payload]); }, }, @@ -49,18 +49,18 @@ jest.mock('Pages/deposit', () => jest.fn(() => 'mockedDeposit')); jest.mock('Pages/withdrawal', () => jest.fn(() => 'mockedWithdrawal')); describe('', () => { - let history; - const renderWithRouter = (component: JSX.Element, mockRootStore: TRootStore) => { + let history: BrowserHistory; + const renderWithRouter = (component: JSX.Element, mock_root_store: ReturnType) => { history = createBrowserHistory(); return { ...render({component}, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }), }; }; it('should show the loading component if client_tnc_status is not yet loaded or not yet logged in', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ common: { routeBackInApp: jest.fn(), is_from_derivgo: true, @@ -103,15 +103,15 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); expect(screen.getByText('mockedLoading')).toBeInTheDocument(); }); it('should render the component if client_tnc_status is loaded', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ common: { routeBackInApp: jest.fn(), is_from_derivgo: true, @@ -155,9 +155,9 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); expect(screen.getByRole('link', { name: 'Deposit' })).toBeInTheDocument(); expect(screen.getByRole('link', { name: 'Withdrawal' })).toBeInTheDocument(); @@ -172,7 +172,7 @@ describe('', () => { it('should go to payment methods page if the learn more about payment methods button is clicked', () => { window.open = jest.fn(); - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ common: { routeBackInApp: jest.fn(), is_from_derivgo: true, @@ -215,9 +215,9 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); const learn_more_btn = screen.getByRole('button', { name: 'Learn more about payment methods' }); fireEvent.click(learn_more_btn); @@ -227,7 +227,7 @@ describe('', () => { // TODO: Fix this test case // it('should redirect to trade page if the close button is clicked ', () => { - // const mockRootStore: DeepPartial = { + // const mock_root_store = mockStore({ // common: { // routeBackInApp: jest.fn(), // is_from_derivgo: true, @@ -267,19 +267,19 @@ describe('', () => { // }, // }, // }, - // }; + // }); - // renderWithRouter(, mockRootStore as TRootStore); + // renderWithRouter(, mock_root_store); // const close_btn = screen.getByTestId('page_overlay_header_close'); // fireEvent.click(close_btn); - // expect(mockRootStore.common!.routeBackInApp).toHaveBeenCalled(); + // expect(mock_root_store.common!.routeBackInApp).toHaveBeenCalled(); // expect(history.location.pathname).toBe('/'); // }); it('should go to selected route page on desktop', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ common: { routeBackInApp: jest.fn(), is_from_derivgo: true, @@ -322,9 +322,9 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); const withdrawal_link = screen.getByRole('link', { name: 'Withdrawal' }); fireEvent.click(withdrawal_link); @@ -333,7 +333,7 @@ describe('', () => { }); it('should not render the side note if on crypto transactions page', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ common: { routeBackInApp: jest.fn(), is_from_derivgo: true, @@ -376,9 +376,9 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); expect(screen.queryByTestId('vertical_tab_side_note')).not.toBeInTheDocument(); }); @@ -386,7 +386,7 @@ describe('', () => { it('should show the selected route page on mobile', () => { (isMobile as jest.Mock).mockReturnValue(true); - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ common: { routeBackInApp: jest.fn(), is_from_derivgo: true, @@ -429,9 +429,9 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); const withdrawal_link = screen.getByRole('link', { name: 'Withdrawal' }); fireEvent.click(withdrawal_link); diff --git a/packages/cashier/src/containers/cashier/cashier.tsx b/packages/cashier/src/containers/cashier/cashier.tsx index ca4236ff4d9a..c5eb722550d7 100644 --- a/packages/cashier/src/containers/cashier/cashier.tsx +++ b/packages/cashier/src/containers/cashier/cashier.tsx @@ -25,7 +25,7 @@ import { TRoute } from '../../types'; import { localize } from '@deriv/translations'; import { observer, useStore } from '@deriv/stores'; import { useCashierStore } from '../../stores/useCashierStores'; -import type { TStores } from '@deriv/stores'; +import { TStores } from '@deriv/stores/types'; import './cashier.scss'; type TCashierProps = RouteComponentProps & { diff --git a/packages/cashier/src/containers/routes/__tests__/route-with-sub-routes.spec.tsx b/packages/cashier/src/containers/routes/__tests__/route-with-sub-routes.spec.tsx index e9f7065abac0..6d29616d67be 100644 --- a/packages/cashier/src/containers/routes/__tests__/route-with-sub-routes.spec.tsx +++ b/packages/cashier/src/containers/routes/__tests__/route-with-sub-routes.spec.tsx @@ -27,6 +27,7 @@ const route = { is_logged_in: true, exact: true, path: '/test-path', + icon_component: '', }; const MockRouteWithSubRoutes = () => ; diff --git a/packages/cashier/src/containers/routes/__tests__/routes.spec.tsx b/packages/cashier/src/containers/routes/__tests__/routes.spec.tsx index fae518c214bc..7da2a6853848 100644 --- a/packages/cashier/src/containers/routes/__tests__/routes.spec.tsx +++ b/packages/cashier/src/containers/routes/__tests__/routes.spec.tsx @@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react'; import Routes from '../routes'; import { Router } from 'react-router'; import { createBrowserHistory } from 'history'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../cashier-providers'; jest.mock('../binary-routes', () => jest.fn(() => 'BinaryRoutes')); @@ -18,7 +18,7 @@ jest.mock('@deriv/components', () => { describe('', () => { it('should show error messages when "has_error = true"', () => { const history = createBrowserHistory(); - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { is_logged_in: false, is_logging_in: false, @@ -36,16 +36,14 @@ describe('', () => { should_show_refresh: true, }, }, - }; + }); render( , { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, } ); @@ -54,7 +52,7 @@ describe('', () => { it('should render component when "has_error = false"', () => { const history = createBrowserHistory(); - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { is_logged_in: false, is_logging_in: false, @@ -62,16 +60,14 @@ describe('', () => { common: { has_error: false, }, - }; + }); render( , { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, } ); diff --git a/packages/cashier/src/containers/routes/error-component/__tests__/error-component.spec.tsx b/packages/cashier/src/containers/routes/error-component/__tests__/error-component.spec.tsx index eac33570fa0b..4129ad0c3dc9 100644 --- a/packages/cashier/src/containers/routes/error-component/__tests__/error-component.spec.tsx +++ b/packages/cashier/src/containers/routes/error-component/__tests__/error-component.spec.tsx @@ -6,7 +6,7 @@ import { createBrowserHistory } from 'history'; describe('', () => { let history; - const renderWithRouter = component => { + const renderWithRouter = (component: React.ReactNode) => { history = createBrowserHistory(); return render({component}); }; diff --git a/packages/cashier/src/containers/routes/error-component/error-component.tsx b/packages/cashier/src/containers/routes/error-component/error-component.tsx index 58911405e379..c826db55784d 100644 --- a/packages/cashier/src/containers/routes/error-component/error-component.tsx +++ b/packages/cashier/src/containers/routes/error-component/error-component.tsx @@ -2,8 +2,8 @@ import React from 'react'; import { useHistory } from 'react-router-dom'; import { PageErrorContainer } from '@deriv/components'; import { routes } from '@deriv/shared'; -import { TStores } from '@deriv/stores'; -import { Localize, localize } from '@deriv/translations'; +import { TRootStore } from '../../../types'; +import { localize, Localize } from '@deriv/translations'; const ErrorComponent = ({ header, @@ -14,7 +14,7 @@ const ErrorComponent = ({ setError, should_clear_error_on_click, should_show_refresh = true, -}: TStores['common']['error']) => { +}: TRootStore['common']['error']) => { const history = useHistory(); React.useEffect(() => { diff --git a/packages/cashier/src/containers/routes/route-with-sub-routes.tsx b/packages/cashier/src/containers/routes/route-with-sub-routes.tsx index d1514880cd1e..920fa15b6c31 100644 --- a/packages/cashier/src/containers/routes/route-with-sub-routes.tsx +++ b/packages/cashier/src/containers/routes/route-with-sub-routes.tsx @@ -9,13 +9,12 @@ import { removeBranchName, default_title, } from '@deriv/shared'; -import { TStores } from '@deriv/stores'; import { getLanguage } from '@deriv/translations'; -import { TRouteConfig, TRoute } from 'Types'; +import { TRootStore, TRouteConfig, TRoute } from '../../types'; type TRouteWithSubRoutesProps = TRouteConfig & { - is_logged_in: TStores['client']['is_logged_in']; - is_logging_in: TStores['client']['is_logging_in']; + is_logged_in: TRootStore['client']['is_logged_in']; + is_logging_in: TRootStore['client']['is_logging_in']; }; type TDefaultSubroute = TRoute | undefined; diff --git a/packages/cashier/src/pages/account-transfer/__tests__/account-transfer.spec.tsx b/packages/cashier/src/pages/account-transfer/__tests__/account-transfer.spec.tsx index 371b8b4995b5..85e2d8a58c4b 100644 --- a/packages/cashier/src/pages/account-transfer/__tests__/account-transfer.spec.tsx +++ b/packages/cashier/src/pages/account-transfer/__tests__/account-transfer.spec.tsx @@ -5,13 +5,13 @@ import { useCashierLocked, useDepositLocked } from '@deriv/hooks'; import { createBrowserHistory } from 'history'; import AccountTransfer from '../account-transfer'; import CashierProviders from '../../../cashier-providers'; -import { mockStore, TStores } from '@deriv/stores'; +import { mockStore } from '@deriv/stores'; jest.mock('@deriv/shared/src/services/ws-methods', () => ({ __esModule: true, default: 'mockedDefaultExport', WS: { - wait: (...payload) => { + wait: (...payload: unknown[]) => { return Promise.resolve([...payload]); }, }, @@ -62,7 +62,7 @@ describe('', () => { onClose: jest.fn(), }; - const renderAccountTransfer = (store: TStores) => { + const renderAccountTransfer = (store: ReturnType) => { render(, { wrapper: ({ children }) => {children}, }); diff --git a/packages/cashier/src/pages/account-transfer/account-transfer-form/__tests__/account-transfer-form.spec.tsx b/packages/cashier/src/pages/account-transfer/account-transfer-form/__tests__/account-transfer-form.spec.tsx index f38932bc95f7..ceb2936351ec 100644 --- a/packages/cashier/src/pages/account-transfer/account-transfer-form/__tests__/account-transfer-form.spec.tsx +++ b/packages/cashier/src/pages/account-transfer/account-transfer-form/__tests__/account-transfer-form.spec.tsx @@ -3,13 +3,15 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { isMobile } from '@deriv/shared'; import AccountTransferForm from '../account-transfer-form'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; +import { TError } from '../../../../types'; jest.mock('@deriv/shared/src/utils/screen/responsive', () => ({ ...jest.requireActual('@deriv/shared/src/utils/screen/responsive'), isMobile: jest.fn(), })); -let mockRootStore; +let mockRootStore: ReturnType; jest.mock('Assets/svgs/trading-platform', () => jest.fn(props =>
TradingPlatformIcon
) @@ -17,7 +19,7 @@ jest.mock('Assets/svgs/trading-platform', () => describe('', () => { beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { account_limits: { daily_transfers: { @@ -29,7 +31,7 @@ describe('', () => { mt5_login_list: [ { login: 'value', - market_type: 'gaming', + market_type: 'financial', server_info: { geolocation: { region: 'region', @@ -93,7 +95,7 @@ describe('', () => { traders_hub: { selected_account: {}, }, - }; + }); }); beforeAll(() => { const modal_root_el = document.createElement('div'); @@ -111,7 +113,7 @@ describe('', () => { error: { code: 'testCode', message: 'testMessage', - }, + } as TError, }; const renderAccountTransferForm = () => { @@ -204,7 +206,7 @@ describe('', () => { props.error = { code: 'Fiat2CryptoTransferOverLimit', message: 'testMessage', - }; + } as TError; renderAccountTransferForm(); @@ -215,7 +217,7 @@ describe('', () => { props.error = { code: 'testCode', message: 'testMessage', - }; + } as TError; renderAccountTransferForm(); diff --git a/packages/cashier/src/pages/account-transfer/account-transfer-form/account-transfer-form-side-note.tsx b/packages/cashier/src/pages/account-transfer/account-transfer-form/account-transfer-form-side-note.tsx index 344a30fa1954..f2eaaa2da1e3 100644 --- a/packages/cashier/src/pages/account-transfer/account-transfer-form/account-transfer-form-side-note.tsx +++ b/packages/cashier/src/pages/account-transfer/account-transfer-form/account-transfer-form-side-note.tsx @@ -3,11 +3,6 @@ import { Localize } from '@deriv/translations'; import { GetLimits } from '@deriv/api-types'; import { Text } from '@deriv/components'; import { getCurrencyDisplayCode, getPlatformSettings } from '@deriv/shared'; -import { TReactChildren } from 'Types'; - -type TAccountTransferBulletProps = { - children: TReactChildren; -}; type TAccountTransferNoteProps = { allowed_transfers_count: GetLimits['daily_transfers']; @@ -22,7 +17,7 @@ type TAccountTransferNoteProps = { transfer_fee?: number | null; }; -const AccountTransferBullet = ({ children }: TAccountTransferBulletProps) => ( +const AccountTransferBullet = ({ children }: React.PropsWithChildren) => (
{children} diff --git a/packages/cashier/src/pages/account-transfer/account-transfer-form/account-transfer-form.tsx b/packages/cashier/src/pages/account-transfer/account-transfer-form/account-transfer-form.tsx index 6f71effba0bb..5188d2819725 100644 --- a/packages/cashier/src/pages/account-transfer/account-transfer-form/account-transfer-form.tsx +++ b/packages/cashier/src/pages/account-transfer/account-transfer-form/account-transfer-form.tsx @@ -66,15 +66,15 @@ const AccountOption = ({ account, idx }: TAccountsList) => { ); }; -let accounts_from: Array = []; -let accounts_to: Array = []; -let derivez_accounts_from: Array = []; -let derivez_accounts_to: Array = []; -let dxtrade_accounts_from: Array = []; -let dxtrade_accounts_to: Array = []; -let mt_accounts_from: Array = []; -let mt_accounts_to: Array = []; -let remaining_transfers: boolean | undefined; +let accounts_from: TAccount[] = []; +let accounts_to: TAccount[] = []; +let derivez_accounts_from: TAccount[] = []; +let derivez_accounts_to: TAccount[] = []; +let dxtrade_accounts_from: TAccount[] = []; +let dxtrade_accounts_to: TAccount[] = []; +let mt_accounts_from: TAccount[] = []; +let mt_accounts_to: TAccount[] = []; +let remaining_transfers: number | undefined; const AccountTransferForm = observer( ({ error, onClickDeposit, onClickNotes, setSideNotes }: TAccountTransferFormProps) => { @@ -150,7 +150,8 @@ const AccountTransferForm = observer( }); if (!is_ok) return message; - if (selected_from.balance && +selected_from.balance < +amount) return localize('Insufficient balance'); + if (selected_from.balance && Number(selected_from.balance) < Number(amount)) + return localize('Insufficient balance'); return undefined; }; @@ -319,7 +320,7 @@ const AccountTransferForm = observer( remaining_transfers = getRemainingTransfers(); const hint = - remaining_transfers && +remaining_transfers === 1 + remaining_transfers && Number(remaining_transfers) === 1 ? localize('You have {{number}} transfer remaining for today.', { number: remaining_transfers }) : localize('You have {{number}} transfers remaining for today.', { number: remaining_transfers }); setTransferToHint(hint); @@ -390,7 +391,7 @@ const AccountTransferForm = observer( }} onSubmit={() => { requestTransferBetweenAccounts({ - amount: account_transfer_amount ? +account_transfer_amount : 0, + amount: account_transfer_amount ? Number(account_transfer_amount) : 0, }); }} validateOnBlur={false} @@ -527,7 +528,7 @@ const AccountTransferForm = observer( >
{ - let mockRootStore; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { is_financial_account: false, is_financial_information_incomplete: false, is_trading_experience_incomplete: false, }, - }; + }); }); it('Should show the default lock content if the account is not financial', () => { diff --git a/packages/cashier/src/pages/account-transfer/account-transfer-no-account/__tests__/account-transfer-no-account.spec.tsx b/packages/cashier/src/pages/account-transfer/account-transfer-no-account/__tests__/account-transfer-no-account.spec.tsx index 7bf8509c4068..21a39c9390b7 100644 --- a/packages/cashier/src/pages/account-transfer/account-transfer-no-account/__tests__/account-transfer-no-account.spec.tsx +++ b/packages/cashier/src/pages/account-transfer/account-transfer-no-account/__tests__/account-transfer-no-account.spec.tsx @@ -5,10 +5,10 @@ import CashierProviders from '../../../../cashier-providers'; import { routes } from '@deriv/shared'; import { createBrowserHistory } from 'history'; import { Router } from 'react-router'; -import { mockStore, TStores } from '@deriv/stores'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let mockRootStore: TStores; + let mockRootStore: ReturnType; const history = createBrowserHistory(); beforeEach(() => { mockRootStore = mockStore({ diff --git a/packages/cashier/src/pages/account-transfer/account-transfer-receipt/__tests__/account-transfer-receipt.spec.tsx b/packages/cashier/src/pages/account-transfer/account-transfer-receipt/__tests__/account-transfer-receipt.spec.tsx index f7310e493100..840558c2321c 100644 --- a/packages/cashier/src/pages/account-transfer/account-transfer-receipt/__tests__/account-transfer-receipt.spec.tsx +++ b/packages/cashier/src/pages/account-transfer/account-transfer-receipt/__tests__/account-transfer-receipt.spec.tsx @@ -5,12 +5,13 @@ import { createBrowserHistory } from 'history'; import { routes } from '@deriv/shared'; import AccountTransferReceipt from '../account-transfer-receipt'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType; const onClose = jest.fn(); beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { loginid: 'CR90000401', switchAccount: jest.fn(), @@ -49,7 +50,7 @@ describe('', () => { }, }, }, - }; + }); }); const history = createBrowserHistory(); diff --git a/packages/cashier/src/pages/deposit/__tests__/deposit.spec.tsx b/packages/cashier/src/pages/deposit/__tests__/deposit.spec.tsx index 2e783e330213..0040c833821f 100644 --- a/packages/cashier/src/pages/deposit/__tests__/deposit.spec.tsx +++ b/packages/cashier/src/pages/deposit/__tests__/deposit.spec.tsx @@ -5,7 +5,6 @@ import { ContentFlag } from '@deriv/shared'; import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../cashier-providers'; import Deposit from '../deposit'; -import { beforeEach } from '@jest/globals'; jest.mock('@deriv/hooks', () => ({ ...jest.requireActual('@deriv/hooks'), @@ -122,7 +121,7 @@ describe('', () => { }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -159,14 +158,14 @@ describe('', () => { }); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Virtual')).toBeInTheDocument(); }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -205,7 +204,7 @@ describe('', () => { (useIsSystemMaintenance as jest.Mock).mockReturnValue(true); const { rerender } = render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('CashierLocked')).toBeInTheDocument(); @@ -220,7 +219,7 @@ describe('', () => { }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -257,14 +256,14 @@ describe('', () => { }); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('FundsProtection')).toBeInTheDocument(); }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -302,14 +301,14 @@ describe('', () => { (useDepositLocked as jest.Mock).mockReturnValue(true); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('DepositLocked')).toBeInTheDocument(); }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -346,14 +345,14 @@ describe('', () => { }); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('CryptoTransactionsHistory')).toBeInTheDocument(); }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -390,14 +389,14 @@ describe('', () => { }); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Error')).toBeInTheDocument(); }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -435,14 +434,14 @@ describe('', () => { }); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('CryptoDeposit')).toBeInTheDocument(); }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -479,14 +478,14 @@ describe('', () => { }); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Real')).toBeInTheDocument(); }); it('should render component', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -523,14 +522,14 @@ describe('', () => { }); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(screen.getByText('CashierOnboarding')).toBeInTheDocument(); }); it('should trigger "setSideNotes" callback', () => { - const mockRootStore = mockStore({ + const mock_root_store = mockStore({ client: { mt5_login_list: [ { @@ -571,7 +570,7 @@ describe('', () => { const setSideNotes = jest.fn(); render(, { - wrapper: ({ children }) => {children}, + wrapper: ({ children }) => {children}, }); expect(setSideNotes).toHaveBeenCalledTimes(2); diff --git a/packages/cashier/src/pages/deposit/crypto-deposit/__tests__/crypto-deposit.spec.tsx b/packages/cashier/src/pages/deposit/crypto-deposit/__tests__/crypto-deposit.spec.tsx index e32e9fa80906..e7f8be0bcd07 100644 --- a/packages/cashier/src/pages/deposit/crypto-deposit/__tests__/crypto-deposit.spec.tsx +++ b/packages/cashier/src/pages/deposit/crypto-deposit/__tests__/crypto-deposit.spec.tsx @@ -4,9 +4,8 @@ import { createBrowserHistory } from 'history'; import { Router } from 'react-router'; import { getCurrencyName, isMobile } from '@deriv/shared'; import CryptoDeposit from '../crypto-deposit'; -import { TRootStore } from 'Types'; -import CashierProviders from '../../../../cashier-providers'; import { mockStore } from '@deriv/stores'; +import CashierProviders from '../../../../cashier-providers'; jest.mock('@deriv/components', () => ({ ...jest.requireActual('@deriv/components'), @@ -48,18 +47,15 @@ jest.mock('@deriv/api', () => { describe('', () => { let history: ReturnType; - const renderWithRouter = (component: JSX.Element, mockRootStore: TRootStore) => { + const renderWithRouter = (component: JSX.Element, mock_root_store: ReturnType) => { history = createBrowserHistory(); return render({component}, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); }; it('should show loader', () => { - // TODO: use mockStore for tests after TStores type will be updated - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { currency: 'BTC', }, @@ -80,15 +76,15 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); expect(screen.getByText('Loading')).toBeInTheDocument(); }); it('should show proper breadcrumbs', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { currency: 'BTC', }, @@ -109,16 +105,16 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); expect(screen.getByText(/cashier/i)).toBeInTheDocument(); expect(screen.getByText(/deposit cryptocurrencies/i)).toBeInTheDocument(); }); it('should trigger setIsDeposit callback when the user clicks on Cashier breadcrumb', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { currency: 'BTC', }, @@ -139,20 +135,20 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); const el_breadcrumb_cashier = screen.queryByText(/cashier/i); if (el_breadcrumb_cashier) { fireEvent.click(el_breadcrumb_cashier); - expect(mockRootStore.modules?.cashier.general_store.setIsDeposit).toHaveBeenCalledWith(false); + expect(mock_root_store.modules?.cashier.general_store.setIsDeposit).toHaveBeenCalledWith(false); } }); it('should show proper error message and button', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { currency: 'BTC', }, @@ -173,9 +169,9 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); expect( screen.getByText( @@ -186,7 +182,7 @@ describe('', () => { }); it('should trigger onClick callback when the user clicks "Refresh" button', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { currency: 'BTC', }, @@ -207,18 +203,18 @@ describe('', () => { }, }, }, - }; + }); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); const refresh_btn = screen.getByRole('button', { name: 'Refresh' }); fireEvent.click(refresh_btn); - expect(mockRootStore.modules!.cashier!.onramp!.pollApiForDepositAddress).toHaveBeenCalledTimes(2); + expect(mock_root_store.modules.cashier.onramp.pollApiForDepositAddress).toHaveBeenCalledTimes(2); }); it('should show proper messages for BTC cryptocurrency', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { currency: 'BTC', }, @@ -239,10 +235,10 @@ describe('', () => { }, }, }, - }; + }); (getCurrencyName as jest.Mock).mockReturnValueOnce('Bitcoin'); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); expect(screen.getByText('Send only Bitcoin (BTC) to this address.')).toBeInTheDocument(); expect( @@ -258,7 +254,7 @@ describe('', () => { }); it('should show proper messages for ETH cryptocurrency', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { currency: 'ETH', }, @@ -279,10 +275,10 @@ describe('', () => { }, }, }, - }; + }); (getCurrencyName as jest.Mock).mockReturnValueOnce('Ethereum'); - renderWithRouter(, mockRootStore as TRootStore); + renderWithRouter(, mock_root_store); expect(screen.getByText('Send only Ethereum (ETH) to this address.')).toBeInTheDocument(); expect( @@ -292,8 +288,8 @@ describe('', () => { }); it('should show proper messages for selected options for ETH, USDC, eUSDT cryptocurrency', () => { - const checkMessagesForOptions = (currency, token) => { - const mockRootStore: DeepPartial = { + const checkMessagesForOptions = (currency: string, token: string) => { + const mock_root_store = mockStore({ client: { currency, }, @@ -314,9 +310,9 @@ describe('', () => { }, }, }, - }; + }); - const { rerender, unmount } = renderWithRouter(, mockRootStore as TRootStore); + const { rerender, unmount } = renderWithRouter(, mock_root_store); const rerenderAndOpenDropdownOptions = () => { rerender( @@ -419,7 +415,7 @@ describe('', () => { }); it('should show "RecentTransactions" in Mobile mode', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { currency: 'BTC', }, @@ -440,7 +436,7 @@ describe('', () => { }, }, }, - }; + }); (isMobile as jest.Mock).mockReturnValue(true); render( @@ -448,9 +444,7 @@ describe('', () => { , { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, } ); diff --git a/packages/cashier/src/pages/deposit/crypto-deposit/crypto-deposit.tsx b/packages/cashier/src/pages/deposit/crypto-deposit/crypto-deposit.tsx index 2709c417697e..f1a69e5f41ba 100644 --- a/packages/cashier/src/pages/deposit/crypto-deposit/crypto-deposit.tsx +++ b/packages/cashier/src/pages/deposit/crypto-deposit/crypto-deposit.tsx @@ -36,11 +36,11 @@ const CryptoDeposit = observer(() => { }, [pollApiForDepositAddress]); const option_list = [ - { text: , value: 1 }, - { text: , value: 2 }, - { text: , value: 3 }, - { text: , value: 4 }, - { text: , value: 5 }, + { text: localize('Binance Smart Chain'), value: '1' }, + { text: localize('Polygon (Matic)'), value: '2' }, + { text: localize('Tron'), value: '3' }, + { text: localize('Ethereum (ERC20)'), value: '4' }, + { text: localize('Ethereum (ETH)'), value: '5' }, ]; const [option_message, setOptionMessage] = useState(''); diff --git a/packages/cashier/src/pages/deposit/deposit-locked/__tests__/deposit-locked.spec.tsx b/packages/cashier/src/pages/deposit/deposit-locked/__tests__/deposit-locked.spec.tsx index 2661a802fb64..22dec2bc4145 100644 --- a/packages/cashier/src/pages/deposit/deposit-locked/__tests__/deposit-locked.spec.tsx +++ b/packages/cashier/src/pages/deposit/deposit-locked/__tests__/deposit-locked.spec.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { Checklist } from '@deriv/components'; import DepositLocked from '../deposit-locked'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../../cashier-providers'; jest.mock('Components/cashier-locked', () => { @@ -20,7 +20,7 @@ jest.mock('Components/cashier-locked', () => { describe('', () => { it('should show the proof of identity document verification message', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { account_status: { cashier_validation: [], @@ -41,12 +41,10 @@ describe('', () => { is_financial_account: false, }, modules: { cashier: { deposit: { onMountDeposit: jest.fn() } } }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByText('To enable this feature you must complete the following:')).toBeInTheDocument(); @@ -54,7 +52,7 @@ describe('', () => { }); it('should show the proof of address document verification message', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { account_status: { cashier_validation: [], @@ -75,12 +73,10 @@ describe('', () => { is_financial_account: false, }, modules: { cashier: { deposit: { onMountDeposit: jest.fn() } } }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByText('To enable this feature you must complete the following:')).toBeInTheDocument(); @@ -88,7 +84,7 @@ describe('', () => { }); it('should show the terms and conditions accept button', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { account_status: { cashier_validation: [], @@ -101,12 +97,10 @@ describe('', () => { is_financial_account: false, }, modules: { cashier: { deposit: { onMountDeposit: jest.fn() } } }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByText('To enable this feature you must complete the following:')).toBeInTheDocument(); @@ -118,7 +112,7 @@ describe('', () => { }); it('should show the financial assessment completion message', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ client: { account_status: { cashier_validation: [], @@ -131,12 +125,10 @@ describe('', () => { is_financial_account: false, }, modules: { cashier: { deposit: { onMountDeposit: jest.fn() } } }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect( diff --git a/packages/cashier/src/pages/index.js b/packages/cashier/src/pages/index.ts similarity index 100% rename from packages/cashier/src/pages/index.js rename to packages/cashier/src/pages/index.ts diff --git a/packages/cashier/src/pages/on-ramp/__tests__/on-ramp.spec.tsx b/packages/cashier/src/pages/on-ramp/__tests__/on-ramp.spec.tsx index 8341aa7d26f0..96ded5522e8f 100644 --- a/packages/cashier/src/pages/on-ramp/__tests__/on-ramp.spec.tsx +++ b/packages/cashier/src/pages/on-ramp/__tests__/on-ramp.spec.tsx @@ -3,29 +3,38 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { isMobile, routes } from '@deriv/shared'; import { useCashierLocked, useDepositLocked } from '@deriv/hooks'; import OnRamp from '../on-ramp'; +import { mockStore } from '@deriv/stores'; import type { TOnRampProps } from '../on-ramp'; import CashierProviders from '../../../cashier-providers'; -import { mockStore, TStores } from '@deriv/stores'; + +jest.mock('@deriv/hooks', () => ({ + ...jest.requireActual('@deriv/hooks'), + useDepositLocked: jest.fn(() => false), +})); jest.mock('@deriv/components', () => { return { - ...(jest.requireActual('@deriv/components') as any), + ...jest.requireActual('@deriv/components'), Loading: () =>
Loading
, ReadMore: () =>
ReadMore
, }; }); + jest.mock('@deriv/shared/src/utils/screen/responsive', () => ({ - ...(jest.requireActual('@deriv/shared/src/utils/screen/responsive') as any), + ...jest.requireActual('@deriv/shared/src/utils/screen/responsive'), isMobile: jest.fn(), })); + jest.mock('Components/cashier-locked', () => { const cashierLocked = () =>
CashierLocked
; return cashierLocked; }); + jest.mock('Pages/on-ramp/on-ramp-provider-card', () => { const onRampProviderCard = () =>
OnRampProviderCard
; return onRampProviderCard; }); + jest.mock('Pages/on-ramp/on-ramp-provider-popup', () => { const onRampProviderPopup = () =>
OnRampProviderPopup
; return onRampProviderPopup; @@ -85,16 +94,16 @@ describe('', () => { mockUseDepositLocked.mockReturnValue(false); mockUseCashierLocked.mockReturnValue(false); }); - const renderOnRamp = (mocked_store: TStores, is_rerender = false) => { - const ui = ( + const mockOnRamp = (mocked_store: ReturnType, is_rerender = false) => { + return ( ); - return is_rerender ? ui : render(ui); }; + it('should render component', () => { - const mockRootStore = mockStore({ + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -114,15 +123,18 @@ describe('', () => { }, }, }); - const { rerender } = renderOnRamp(mockRootStore) as ReturnType; + const { rerender } = render(mockOnRamp(mock)); + expect(screen.getByText('Loading')).toBeInTheDocument(); - mockRootStore.modules.cashier.general_store.is_loading = false; - mockRootStore.client.is_switching = true; - rerender(renderOnRamp(mockRootStore, true) as JSX.Element); + mock.modules.cashier.general_store.is_loading = false; + mock.client.is_switching = true; + rerender(mockOnRamp(mock)); expect(screen.getByText('Loading')).toBeInTheDocument(); }); + it('should render component', () => { - const mockRootStore = mockStore({ + (useCashierLocked as jest.Mock).mockReturnValue(true); + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -134,15 +146,19 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - mockUseCashierLocked.mockReturnValue(true); - const { rerender } = renderOnRamp(mockRootStore) as ReturnType; + const { rerender } = render(mockOnRamp(mock)); + expect(screen.getByText('CashierLocked')).toBeInTheDocument(); - mockUseDepositLocked.mockReturnValue(true); - rerender(renderOnRamp(mockRootStore, true) as JSX.Element); + + (useCashierLocked as jest.Mock).mockReturnValue(false); + (useDepositLocked as jest.Mock).mockReturnValue(true); + rerender(mockOnRamp(mock)); + expect(screen.getByText('CashierLocked')).toBeInTheDocument(); }); + it('should render component and "Select payment channel" message', () => { - const mockRootStore = mockStore({ + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -154,15 +170,17 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - renderOnRamp(mockRootStore); + render(mockOnRamp(mock)); + expect(screen.getByText('Select payment channel')).toBeInTheDocument(); expect(screen.getByText('OnRampProviderCard')).toBeInTheDocument(); }); + it('should render component with proper title and component', () => { const modal_root_el = document.createElement('div'); modal_root_el.setAttribute('id', 'modal_root'); document.body.appendChild(modal_root_el); - const mockRootStore = mockStore({ + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -182,7 +200,8 @@ describe('', () => { }, }, }); - renderOnRamp(mockRootStore); + render(mockOnRamp(mock)); + expect(screen.getByText('Title of the onramp popup modal')).toBeInTheDocument(); expect(screen.getByText('OnRampProviderPopup')).toBeInTheDocument(); document.body.removeChild(modal_root_el); @@ -192,7 +211,7 @@ describe('', () => { const modal_root_el = document.createElement('div'); modal_root_el.setAttribute('id', 'modal_root'); document.body.appendChild(modal_root_el); - const mockRootStore = mockStore({ + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -212,14 +231,16 @@ describe('', () => { }, }, }); - renderOnRamp(mockRootStore); + render(mockOnRamp(mock)); const close_cross_btn = screen.getByRole('button', { name: '' }); fireEvent.click(close_cross_btn); - expect(mockRootStore.modules.cashier.onramp.setIsOnRampModalOpen).toHaveBeenCalledWith(false); + + expect(mock.modules.cashier.onramp.setIsOnRampModalOpen).toHaveBeenCalledWith(false); document.body.removeChild(modal_root_el); }); + it('should trigger "setSideNotes" callback in Desktop mode', () => { - const mockRootStore = mockStore({ + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -231,12 +252,14 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - renderOnRamp(mockRootStore); + render(mockOnRamp(mock)); + expect(props.setSideNotes).toHaveBeenCalledTimes(1); }); + it('should show "What is Fiat onramp?" message and render component in Mobile mode', () => { (isMobile as jest.Mock).mockReturnValue(true); - const mockRootStore = mockStore({ + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -248,13 +271,15 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - renderOnRamp(mockRootStore); + render(mockOnRamp(mock)); + expect(screen.getByText('What is Fiat onramp?')).toBeInTheDocument(); expect(screen.getByText('ReadMore')).toBeInTheDocument(); }); + it('should have proper menu options in Mobile mode', () => { (isMobile as jest.Mock).mockReturnValue(true); - const mockRootStore = mockStore({ + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -266,12 +291,15 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - renderOnRamp(mockRootStore); + render(mockOnRamp(mock)); + const select = screen.getByTestId('dt_on_ramp_select_native'); const labels = Array.from(select as any).map((option: any) => option.label); + expect(labels).toContain('Deposit'); expect(labels).toContain('Transfer'); }); + it('should trigger "routeTo" callback when the user chooses a different from "Fiat onramp" option in Mobile mode', () => { (isMobile as jest.Mock).mockReturnValue(true); props.menu_options = [ @@ -288,7 +316,7 @@ describe('', () => { path: routes.cashier_onramp, }, ]; - const mockRootStore = mockStore({ + const mock = mockStore({ client: { account_status: { status: [] }, mt5_login_list: [ @@ -300,9 +328,10 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - renderOnRamp(mockRootStore); + render(mockOnRamp(mock)); const select = screen.getByTestId('dt_on_ramp_select_native'); fireEvent.change(select, { target: { value: routes.cashier_deposit } }); - expect(mockRootStore.common.routeTo).toHaveBeenCalledTimes(1); + + expect(mock.common.routeTo).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/cashier/src/pages/on-ramp/on-ramp-provider-card/__tests__/on-ramp-provider-card.spec.tsx b/packages/cashier/src/pages/on-ramp/on-ramp-provider-card/__tests__/on-ramp-provider-card.spec.tsx index 5679aebd8b10..348d5a4063b0 100644 --- a/packages/cashier/src/pages/on-ramp/on-ramp-provider-card/__tests__/on-ramp-provider-card.spec.tsx +++ b/packages/cashier/src/pages/on-ramp/on-ramp-provider-card/__tests__/on-ramp-provider-card.spec.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import OnRampProviderCard from '../on-ramp-provider-card'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../../cashier-providers'; describe('', () => { @@ -19,15 +19,15 @@ describe('', () => { getAllowedResidencies: jest.fn(() => []), getScriptDependencies: jest.fn(() => []), getDefaultFromCurrency: jest.fn(() => ''), - getFromCurrencies: jest.fn(() => ''), - getToCurrencies: jest.fn(() => ''), + getFromCurrencies: jest.fn(() => []), + getToCurrencies: jest.fn(() => []), getWidgetHtml: jest.fn(() => Promise.resolve()), onMountWidgetContainer: jest.fn(), should_show_deposit_address: false, }; it('should show proper messages and button', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: false, is_mobile: false, @@ -39,12 +39,10 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Banxa')).toBeInTheDocument(); @@ -57,7 +55,7 @@ describe('', () => { }); it('should show proper icons in dark_mode', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, is_mobile: false, @@ -69,12 +67,10 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByTestId('dti_provider_icon_dark')).toBeInTheDocument(); @@ -82,7 +78,7 @@ describe('', () => { }); it('should trigger onClick callback, when "Select" button is clicked', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: false, is_mobile: false, @@ -94,17 +90,15 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const btn = screen.getByRole('button', { name: 'Select' }); fireEvent.click(btn); - expect(mockRootStore.modules!.cashier!.onramp!.setSelectedProvider).toHaveBeenCalledTimes(1); + expect(mock_root_store.modules.cashier.onramp.setSelectedProvider).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/cashier/src/pages/on-ramp/on-ramp-provider-popup/__tests__/on-ramp-provider-popup.spec.tsx b/packages/cashier/src/pages/on-ramp/on-ramp-provider-popup/__tests__/on-ramp-provider-popup.spec.tsx index 861eb6b237d2..4ebb959218fe 100644 --- a/packages/cashier/src/pages/on-ramp/on-ramp-provider-popup/__tests__/on-ramp-provider-popup.spec.tsx +++ b/packages/cashier/src/pages/on-ramp/on-ramp-provider-popup/__tests__/on-ramp-provider-popup.spec.tsx @@ -1,35 +1,17 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import OnRampProviderPopup from '../on-ramp-provider-popup'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import CashierProviders from '../../../../cashier-providers'; jest.mock('@deriv/components', () => ({ - ...(jest.requireActual('@deriv/components') as any), + ...jest.requireActual('@deriv/components'), Loading: () =>
Loading
, })); describe('', () => { - const props = { - api_error: '', - deposit_address: 'tb1qhux20f7h42ya9nqdntl6r9p7p264a2ct8t3n6p', - is_deposit_address_loading: false, - is_requesting_widget_html: false, - selected_provider: { - name: 'Banxa', - should_show_deposit_address: true, - onMountWidgetContainer: jest.fn(), - }, - should_show_dialog: false, - should_show_widget: false, - widget_error: '', - widget_html: 'Widget HTML', - onClickDisclaimerContinue: jest.fn(), - onClickGoToDepositPage: jest.fn(), - setIsOnRampModalOpen: jest.fn(), - }; it('should not render component', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -51,19 +33,17 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.queryByTestId('dti_on-ramp_popup')).not.toBeInTheDocument(); }); it('should show loader', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -89,19 +69,17 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Loading')).toBeInTheDocument(); }); it('should show widget', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -127,19 +105,17 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Widget error')).toBeInTheDocument(); }); it('should show dialog with proper messages and buttons', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -165,19 +141,17 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect(screen.getByText('Please go to the Deposit page to get an address.')).toBeInTheDocument(); }); it('should show dialog with proper messages and buttons', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -203,12 +177,10 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect( @@ -221,7 +193,7 @@ describe('', () => { }); it('should trigger onClick callbacks in dialog when the user clicks "Cancel" and "Go to Deposit page" buttons', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -247,27 +219,25 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const cancel_btn = screen.getByRole('button', { name: 'Cancel' }); fireEvent.click(cancel_btn); - expect(mockRootStore.modules!.cashier!.onramp.setIsOnRampModalOpen).toHaveBeenCalledTimes(1); + expect(mock_root_store.modules.cashier.onramp.setIsOnRampModalOpen).toHaveBeenCalledTimes(1); const go_to_deposit_page_btn = screen.getByRole('button', { name: 'Go to Deposit page' }); fireEvent.click(go_to_deposit_page_btn); - expect(mockRootStore.modules!.cashier!.onramp.onClickGoToDepositPage).toHaveBeenCalledTimes(1); + expect(mock_root_store.modules.cashier.onramp.onClickGoToDepositPage).toHaveBeenCalledTimes(1); }); it('should show proper messages and buttons', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -293,12 +263,10 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); expect( @@ -320,7 +288,7 @@ describe('', () => { }); it('should trigger onFocus method when the user clicks on deposit address field', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -346,12 +314,10 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const deposit_address_input = screen.getByRole('textbox'); @@ -359,7 +325,7 @@ describe('', () => { }); it('should trigger onClick calbacks when the user clicks on "Cancel" and "Continue" buttons', () => { - const mockRootStore: DeepPartial = { + const mock_root_store = mockStore({ ui: { is_dark_mode_on: true, }, @@ -385,22 +351,20 @@ describe('', () => { }, }, }, - }; + }); render(, { - wrapper: ({ children }) => ( - {children} - ), + wrapper: ({ children }) => {children}, }); const cancel_btn = screen.getByRole('button', { name: 'Cancel' }); fireEvent.click(cancel_btn); - expect(mockRootStore.modules!.cashier!.onramp.setIsOnRampModalOpen).toHaveBeenCalledTimes(1); + expect(mock_root_store.modules.cashier.onramp.setIsOnRampModalOpen).toHaveBeenCalledTimes(1); const continue_btn = screen.getByRole('button', { name: 'Continue' }); fireEvent.click(continue_btn); - expect(mockRootStore.modules!.cashier!.onramp.onClickDisclaimerContinue).toHaveBeenCalledTimes(1); + expect(mock_root_store.modules.cashier.onramp.onClickDisclaimerContinue).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/cashier/src/pages/on-ramp/on-ramp.tsx b/packages/cashier/src/pages/on-ramp/on-ramp.tsx index 368412878098..061443f49e10 100644 --- a/packages/cashier/src/pages/on-ramp/on-ramp.tsx +++ b/packages/cashier/src/pages/on-ramp/on-ramp.tsx @@ -6,7 +6,6 @@ import { Localize, localize } from '@deriv/translations'; import { useStore, observer } from '@deriv/stores'; import CashierLocked from '../../components/cashier-locked'; import SideNote from '../../components/side-note'; -import { TReactFormEvent } from '../../types'; import OnRampProviderCard from './on-ramp-provider-card'; import OnRampProviderPopup from './on-ramp-provider-popup'; import { useCashierStore } from '../../stores/useCashierStores'; @@ -118,9 +117,11 @@ const OnRamp = observer(({ menu_options, setSideNotes }: TOnRampProps) => { { if (e.currentTarget.value !== selected_cashier_path) { setSelectedCashierPath(e.currentTarget.value); diff --git a/packages/cashier/src/pages/payment-agent-transfer/__tests__/payment-agent-transfer.spec.tsx b/packages/cashier/src/pages/payment-agent-transfer/__tests__/payment-agent-transfer.spec.tsx index ec665ed265bc..208758a6ca7d 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/__tests__/payment-agent-transfer.spec.tsx +++ b/packages/cashier/src/pages/payment-agent-transfer/__tests__/payment-agent-transfer.spec.tsx @@ -5,7 +5,7 @@ import { createBrowserHistory } from 'history'; import PaymentAgentTransfer from '../payment-agent-transfer'; import CashierProviders from '../../../cashier-providers'; import { useCashierLocked } from '@deriv/hooks'; -import { mockStore, TStores } from '@deriv/stores'; +import { mockStore } from '@deriv/stores'; jest.mock('@deriv/components', () => { const original_module = jest.requireActual('@deriv/components'); @@ -66,7 +66,7 @@ describe('', () => { mockUseCashierLocked.mockReturnValue(false); }); - const renderPaymentAgentTransfer = (mock_root_store: TStores) => { + const renderPaymentAgentTransfer = (mock_root_store: ReturnType) => { return render( diff --git a/packages/cashier/src/pages/payment-agent-transfer/index.js b/packages/cashier/src/pages/payment-agent-transfer/index.js deleted file mode 100644 index 185c707c7db1..000000000000 --- a/packages/cashier/src/pages/payment-agent-transfer/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import PaymentAgentTransfer from './payment-agent-transfer.jsx'; - -export default PaymentAgentTransfer; diff --git a/packages/cashier/src/pages/payment-agent-transfer/index.ts b/packages/cashier/src/pages/payment-agent-transfer/index.ts new file mode 100644 index 000000000000..3f8c14e6417e --- /dev/null +++ b/packages/cashier/src/pages/payment-agent-transfer/index.ts @@ -0,0 +1,3 @@ +import PaymentAgentTransfer from './payment-agent-transfer'; + +export default PaymentAgentTransfer; diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/__tests__/payment-agent-transfer-confirm.spec.js b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/__tests__/payment-agent-transfer-confirm.spec.tsx similarity index 92% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/__tests__/payment-agent-transfer-confirm.spec.js rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/__tests__/payment-agent-transfer-confirm.spec.tsx index 5b9f91820a7d..8fe455e43dd8 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/__tests__/payment-agent-transfer-confirm.spec.js +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/__tests__/payment-agent-transfer-confirm.spec.tsx @@ -1,24 +1,25 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { fireEvent, render, screen } from '@testing-library/react'; -import PaymentAgentTransferConfirm from '../payment-agent-transfer-confirm.jsx'; +import PaymentAgentTransferConfirm from '../payment-agent-transfer-confirm'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType; beforeAll(() => { ReactDOM.createPortal = jest.fn(component => { - return component; + return component as React.ReactPortal; }); }); afterAll(() => { - ReactDOM.createPortal.mockClear(); + (ReactDOM.createPortal as jest.Mock).mockClear(); }); beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ ui: { disableApp: jest.fn(), enableApp: jest.fn(), @@ -42,7 +43,7 @@ describe('', () => { }, }, }, - }; + }); }); const renderPaymentAgentTransferConfirm = () => { diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/index.js b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/index.ts similarity index 87% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/index.js rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/index.ts index 9f41ffab58e8..42ef7d01ce25 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/index.js +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/index.ts @@ -1,3 +1,3 @@ -import PaymentAgentTransferConfirm from './payment-agent-transfer-confirm.jsx'; +import PaymentAgentTransferConfirm from './payment-agent-transfer-confirm'; export default PaymentAgentTransferConfirm; diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/payment-agent-transfer-confirm.jsx b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/payment-agent-transfer-confirm.tsx similarity index 79% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/payment-agent-transfer-confirm.jsx rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/payment-agent-transfer-confirm.tsx index 37048f1d95f6..e61c011baea3 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/payment-agent-transfer-confirm.jsx +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-confirm/payment-agent-transfer-confirm.tsx @@ -19,10 +19,10 @@ const PaymentAgentTransferConfirm = observer(() => { return ( { value: , key: 'amount', }, - { label: localize('Description'), value: description, key: 'description' }, + { label: localize('Description'), value: description || '', key: 'description' }, ]} error={error} - is_payment_agent_transfer onClickBack={() => { setIsTryTransferSuccessful(false); }} onClickConfirm={() => { - requestPaymentAgentTransfer({ amount, currency, description, transfer_to }); + requestPaymentAgentTransfer({ + amount: Number(amount), + currency, + description: description || '', + transfer_to: transfer_to || '', + }); }} /> ); diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/__tests__/payment-agent-transfer-form.spec.js b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/__tests__/payment-agent-transfer-form.spec.tsx similarity index 73% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/__tests__/payment-agent-transfer-form.spec.js rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/__tests__/payment-agent-transfer-form.spec.tsx index 620955e86eed..cde8d88c8b9d 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/__tests__/payment-agent-transfer-form.spec.js +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/__tests__/payment-agent-transfer-form.spec.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import PaymentAgentTransferForm from '../payment-agent-transfer-form'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; +import { expect } from '@jest/globals'; jest.mock('@deriv/shared/src/utils/validation/declarative-validation-rules', () => ({ __esModule: true, @@ -10,10 +12,10 @@ jest.mock('@deriv/shared/src/utils/validation/declarative-validation-rules', () })); describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType, modal_root_el: HTMLDivElement; beforeAll(() => { - const modal_root_el = document.createElement('div'); + modal_root_el = document.createElement('div'); modal_root_el.setAttribute('id', 'modal_root'); document.body.appendChild(modal_root_el); }); @@ -23,7 +25,7 @@ describe('', () => { }); beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ ui: { disableApp: jest.fn(), enableApp: jest.fn(), @@ -45,13 +47,13 @@ describe('', () => { setErrorMessage: jest.fn(), }, transfer_limit: { - min: '1', - max: '100', + min_withdrawal: '1', + max_withdrawal: '100', }, }, }, }, - }; + }); }); const renderPaymentAgentTransferForm = () => { @@ -63,27 +65,22 @@ describe('', () => { }; it('should render the component', () => { - const { container } = renderPaymentAgentTransferForm(); + renderPaymentAgentTransferForm(); - expect(container.firstChild).toHaveClass('payment-agent-transfer-form__container'); + expect(screen.getByTestId('dt_payment_agent_transfer_form_container')).toBeInTheDocument(); }); - it('should show an error if client login id or amount is not provided', async () => { + it('should have the submit button disabled if no data is filled (initially)', async () => { renderPaymentAgentTransferForm(); const submit_button = screen.getByRole('button'); - fireEvent.click(submit_button); - - await waitFor(() => { - expect(screen.getByText('Please enter a valid client login ID.')).toBeInTheDocument(); - expect(screen.getByText('This field is required.')).toBeInTheDocument(); - }); + expect(submit_button).toBeDisabled(); }); it('should show an error if the login id is not valid', async () => { - const { container } = renderPaymentAgentTransferForm(); + renderPaymentAgentTransferForm(); - const loginid_field = container.querySelector('input[name=loginid]'); + const loginid_field = screen.getByTestId('dt_payment_agent_transfer_form_input_loginid'); const submit_button = screen.getByRole('button'); fireEvent.change(loginid_field, { target: { value: 'abc' } }); @@ -95,9 +92,9 @@ describe('', () => { }); it('should show an error if amount to be transferred is greater than the balance', async () => { - const { container } = renderPaymentAgentTransferForm(); + renderPaymentAgentTransferForm(); - const amount_field = container.querySelector('input[name=amount]'); + const amount_field = screen.getByTestId('dt_payment_agent_transfer_form_input_amount'); const submit_button = screen.getByRole('button'); fireEvent.change(amount_field, { target: { value: '90' } }); @@ -109,9 +106,9 @@ describe('', () => { }); it('should show an error if description is not valid', async () => { - const { container } = renderPaymentAgentTransferForm(); + renderPaymentAgentTransferForm(); - const description_field = container.querySelector('[name=description]'); + const description_field = screen.getByTestId('dt_payment_agent_transfer_form_input_description'); const submit_button = screen.getByRole('button'); fireEvent.change(description_field, { target: { value: '%' } }); diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/index.js b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/index.ts similarity index 91% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/index.js rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/index.ts index 8b518efbe059..118556c0dea4 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/index.js +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/index.ts @@ -1,3 +1,3 @@ -import PaymentAgentTransferForm from './payment-agent-transfer-form.jsx'; +import PaymentAgentTransferForm from './payment-agent-transfer-form'; export default PaymentAgentTransferForm; diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/payment-agent-transfer-form.jsx b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/payment-agent-transfer-form.tsx similarity index 67% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/payment-agent-transfer-form.jsx rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/payment-agent-transfer-form.tsx index 06474cd0798e..d70dc144d4b8 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/payment-agent-transfer-form.jsx +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-form/payment-agent-transfer-form.tsx @@ -1,35 +1,50 @@ import classNames from 'classnames'; import React from 'react'; -import { Field, Formik, Form } from 'formik'; +import { Field, FieldProps, Formik, Form } from 'formik'; import { Button, DesktopWrapper, Input, Text } from '@deriv/components'; import { getDecimalPlaces, validNumber, getCurrencyDisplayCode } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; import { localize, Localize } from '@deriv/translations'; -import ErrorDialog from 'Components/error-dialog'; +import ErrorDialog from '../../../components/error-dialog'; import { useCashierStore } from '../../../stores/useCashierStores'; import './payment-agent-transfer-form.scss'; +import { TTransferLimit } from '../../../types'; -const validateTransfer = (values, { balance, currency, transfer_limit }) => { - const errors = {}; +type TValidateTransferProps = { + balance: string; + currency: string; + transfer_limit: TTransferLimit; +}; + +const validateTransfer = ( + values: { loginid: string; amount: string; description: string }, + { balance, currency, transfer_limit }: TValidateTransferProps +) => { + const errors = { loginid: '', amount: '', description: '' }; - if (!values.loginid || !/^[A-Za-z]+[0-9]+$/.test(values.loginid)) { + if (!values.loginid || !/^[A-Za-z]+\d+$/.test(values.loginid)) { errors.loginid = localize('Please enter a valid client login ID.'); } const { is_ok, message } = validNumber(values.amount, { type: 'float', decimals: getDecimalPlaces(currency), - ...(transfer_limit.min && { - min: transfer_limit.min, - max: +balance >= transfer_limit.min && +balance < transfer_limit.max ? balance : transfer_limit.max, + ...(transfer_limit.min_withdrawal && { + min: Number(transfer_limit.min_withdrawal), + max: + Number(balance) >= Number(transfer_limit.min_withdrawal) && + transfer_limit.max_withdrawal && + Number(balance) < Number(transfer_limit.max_withdrawal) + ? Number(balance) + : Number(transfer_limit.max_withdrawal), }), }); if (!values.amount) { errors.amount = localize('This field is required.'); - } else if (+balance < +values.amount) { + } else if (Number(balance) < Number(values.amount)) { errors.amount = localize('Insufficient balance.'); - } else if (!is_ok) { + } else if (!is_ok && message) { errors.amount = message; } @@ -37,7 +52,7 @@ const validateTransfer = (values, { balance, currency, transfer_limit }) => { errors.description = localize('Please enter a valid description.'); } - return errors; + return Object.fromEntries(Object.entries(errors).filter(([_, v]) => !!v)); }; const PaymentAgentTransferForm = observer(() => { @@ -52,16 +67,19 @@ const PaymentAgentTransferForm = observer(() => { } = payment_agent_transfer_store; const { setErrorMessage } = error; - const validateTransferPassthrough = values => + const validateTransferPassthrough = (values: { loginid: string; amount: string; description: string }) => validateTransfer(values, { - balance, + balance: balance !== undefined ? String(balance) : '', currency, transfer_limit, }); - const onTransferPassthrough = async (values, actions) => { + const onTransferPassthrough = async ( + values: { loginid: string; amount: string; description: string }, + actions: { setSubmitting: (value: boolean) => void } + ) => { const payment_agent_transfer = await requestTryPaymentAgentTransfer({ - amount: values.amount, + amount: Number(values.amount), currency, description: values.description.replace(/\n/g, ' '), transfer_to: values.loginid, @@ -71,8 +89,18 @@ const PaymentAgentTransferForm = observer(() => { } }; + // in case coming back from confirmation screen, populate the recent data to be edited + const initial_transfer_form_values = { + loginid: transfer_to || '', + amount: amount?.toString() || '', + description: description || '', + }; + return ( -
+
{ {({ errors, isSubmitting, isValid, touched, handleChange }) => ( - {({ field }) => ( + {({ field }: FieldProps) => ( { - setErrorMessage(''); + setErrorMessage({ code: '', message: '' }); handleChange(e); }} className='payment-agent-transfer-form__input' + data-testid='dt_payment_agent_transfer_form_input_loginid' type='text' label={localize('Client account number')} - error={touched.loginid && errors.loginid} + error={(touched.loginid && errors.loginid) || ''} required autoComplete='off' maxLength={20} @@ -115,17 +140,18 @@ const PaymentAgentTransferForm = observer(() => { )} - {({ field }) => ( + {({ field }: FieldProps) => ( { - setErrorMessage(''); + setErrorMessage({ code: '', message: '' }); handleChange(e); }} className='payment-agent-transfer-form__input dc-input--no-placeholder' + data-testid='dt_payment_agent_transfer_form_input_amount' type='text' label={localize('Amount')} - error={touched.amount && errors.amount} + error={(touched.amount && errors.amount) || ''} required trailing_icon={ { )} - {({ field }) => ( + {({ field }: FieldProps) => ( { - setErrorMessage(''); + setErrorMessage({ code: '', message: '' }); handleChange(e); }} className='payment-agent-transfer-form__input-area' + data-testid='dt_payment_agent_transfer_form_input_description' type='textarea' label={localize('Description')} error={errors.description} diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/__tests__/payment-agent-transfer-receipt.spec.js b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/__tests__/payment-agent-transfer-receipt.spec.tsx similarity index 83% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/__tests__/payment-agent-transfer-receipt.spec.js rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/__tests__/payment-agent-transfer-receipt.spec.tsx index 556ec33a3cae..2273a2b64aaf 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/__tests__/payment-agent-transfer-receipt.spec.js +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/__tests__/payment-agent-transfer-receipt.spec.tsx @@ -1,16 +1,17 @@ import React from 'react'; import PaymentAgentTransferReceipt from '../payment-agent-transfer-receipt'; import { fireEvent, render, screen } from '@testing-library/react'; -import { createBrowserHistory } from 'history'; +import { BrowserHistory, createBrowserHistory } from 'history'; import { Router } from 'react-router'; import { routes } from '@deriv/shared'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let history, mockRootStore; + let history: BrowserHistory, mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { currency: 'USD', }, @@ -25,7 +26,7 @@ describe('', () => { }, }, }, - }; + }); history = createBrowserHistory(); }); @@ -41,9 +42,9 @@ describe('', () => { }; it('component should render', () => { - const { container } = renderPaymentAgentTransferReceipt(); + renderPaymentAgentTransferReceipt(); - expect(container.querySelector('.payment-agent-transfer-receipt__wrapper')).toBeInTheDocument(); + expect(screen.getByTestId('dt_payment_agent_transfer_receipt_wrapper')).toBeInTheDocument(); }); it(`should redirect to statement page when click on 'View in statement' button`, () => { diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/index.js b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/index.ts similarity index 87% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/index.js rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/index.ts index a6e623f7f726..ef7ba2a63a05 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/index.js +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/index.ts @@ -1,3 +1,3 @@ -import PaymentAgentTransferReceipt from './payment-agent-transfer-receipt.jsx'; +import PaymentAgentTransferReceipt from './payment-agent-transfer-receipt'; export default PaymentAgentTransferReceipt; diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/payment-agent-transfer-receipt.jsx b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/payment-agent-transfer-receipt.tsx similarity index 83% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/payment-agent-transfer-receipt.jsx rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/payment-agent-transfer-receipt.tsx index adce13194782..b0f344a01472 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/payment-agent-transfer-receipt.jsx +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer-receipt/payment-agent-transfer-receipt.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import { withRouter } from 'react-router'; import { Button, Icon, Text } from '@deriv/components'; @@ -7,13 +6,18 @@ import { observer, useStore } from '@deriv/stores'; import { localize, Localize } from '@deriv/translations'; import { useCashierStore } from '../../../stores/useCashierStores'; import './payment-agent-transfer-receipt.scss'; +import { BrowserHistory } from 'history'; -const openStatement = (history, resetPaymentAgentTransfer) => { +const openStatement = (history: BrowserHistory, resetPaymentAgentTransfer: VoidFunction) => { history.push(routes.statement); resetPaymentAgentTransfer(); }; -const PaymentAgentTransferReceipt = observer(({ history }) => { +type TPaymentAgentTransferReceipt = { + history: BrowserHistory; +}; + +const PaymentAgentTransferReceipt = observer(({ history }: TPaymentAgentTransferReceipt) => { const { client, common } = useStore(); const { currency, loginid } = client; const { is_from_derivgo } = common; @@ -21,11 +25,14 @@ const PaymentAgentTransferReceipt = observer(({ history }) => { const { receipt, resetPaymentAgentTransfer } = payment_agent_transfer; return ( -
+
{' '} - {formatMoney(currency, receipt.amount_transferred, true)} {getCurrencyDisplayCode(currency)} + {formatMoney(currency, Number(receipt.amount_transferred), true)} {getCurrencyDisplayCode(currency)}
@@ -47,7 +54,7 @@ const PaymentAgentTransferReceipt = observer(({ history }) => { {receipt.client_name} - {receipt.client_id.toUpperCase()} + {receipt.client_id?.toUpperCase() || ''} @@ -77,8 +84,4 @@ const PaymentAgentTransferReceipt = observer(({ history }) => { ); }); -PaymentAgentTransferReceipt.propTypes = { - history: PropTypes.object, -}; - export default withRouter(PaymentAgentTransferReceipt); diff --git a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer.jsx b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer.tsx similarity index 98% rename from packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer.jsx rename to packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer.tsx index 41728c1b1cd6..027163123fd9 100644 --- a/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer.jsx +++ b/packages/cashier/src/pages/payment-agent-transfer/payment-agent-transfer.tsx @@ -53,7 +53,7 @@ const PaymentAgentTransfer = observer(() => { // for others show them at the bottom of the form next to submit button return ; } - if (!+balance) { + if (!Number(balance)) { return ; } if (is_try_transfer_successful) { diff --git a/packages/cashier/src/pages/payment-agent/__tests__/payment-agent.spec.tsx b/packages/cashier/src/pages/payment-agent/__tests__/payment-agent.spec.tsx index 70717646cfa5..662e66751cd5 100644 --- a/packages/cashier/src/pages/payment-agent/__tests__/payment-agent.spec.tsx +++ b/packages/cashier/src/pages/payment-agent/__tests__/payment-agent.spec.tsx @@ -4,8 +4,8 @@ import { Router } from 'react-router'; import { createBrowserHistory } from 'history'; import PaymentAgent from '../payment-agent'; import CashierProviders from '../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; import { useCashierLocked } from '@deriv/hooks'; -import { mockStore, TStores } from '@deriv/stores'; jest.mock('@deriv/components', () => { const original_module = jest.requireActual('@deriv/components'); @@ -38,7 +38,7 @@ const cashier_mock = { }; describe('', () => { - const renderPaymentAgent = (mock_root_store: TStores) => { + const renderPaymentAgent = (mock_root_store: ReturnType) => { return render( diff --git a/packages/cashier/src/pages/payment-agent/index.js b/packages/cashier/src/pages/payment-agent/index.js deleted file mode 100644 index 2615c8d06143..000000000000 --- a/packages/cashier/src/pages/payment-agent/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import PaymentAgent from './payment-agent.jsx'; - -export default PaymentAgent; diff --git a/packages/cashier/src/pages/payment-agent/index.ts b/packages/cashier/src/pages/payment-agent/index.ts new file mode 100644 index 000000000000..d3480e206276 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/index.ts @@ -0,0 +1,3 @@ +import PaymentAgent from './payment-agent'; + +export default PaymentAgent; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card-description.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card-description.spec.tsx similarity index 63% rename from packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card-description.spec.js rename to packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card-description.spec.tsx index a65b566c4bfa..78670e523189 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card-description.spec.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card-description.spec.tsx @@ -1,13 +1,24 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import PaymentAgentCardDescription from '../payment-agent-card-description'; +import { TPaymentAgent } from '../../../../types'; describe('', () => { - const mocked_payment_agent = { + const mocked_payment_agent: TPaymentAgent = { + currencies: 'USD', + deposit_commission: '', + email: '', further_information: 'further information', + max_withdrawal: '', + min_withdrawal: '', name: 'Payment Agent of CR90000000', + paymentagent_loginid: '', + phone_numbers: [], + summary: '', supported_banks: [{ payment_method: 'Visa' }], - urls: [{ url: 'http://www.MyPAMyAdventure2.com/' }, { url: 'http://www.MyPAMyAdventure.com/' }], + supported_payment_methods: [], + urls: [{ url: 'https://www.MyPAMyAdventure2.com/' }, { url: 'https://www.MyPAMyAdventure.com/' }], + withdrawal_commission: '', }; it('should show proper description details and icon', () => { @@ -15,8 +26,8 @@ describe('', () => { expect(screen.getByText('Payment Agent of CR90000000')).toBeInTheDocument(); expect(screen.getByText('Further information')).toBeInTheDocument(); - expect(screen.getByText('http://www.MyPAMyAdventure2.com/,')).toBeInTheDocument(); - expect(screen.getByText('http://www.MyPAMyAdventure.com/')).toBeInTheDocument(); + expect(screen.getByText('https://www.MyPAMyAdventure2.com/,')).toBeInTheDocument(); + expect(screen.getByText('https://www.MyPAMyAdventure.com/')).toBeInTheDocument(); expect(screen.getByTestId('dt_payment_method_icon')).toBeInTheDocument(); }); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card.spec.js deleted file mode 100644 index 1c862c3e4829..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card.spec.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import PaymentAgentCard from '../payment-agent-card'; - -jest.mock('@deriv/components', () => ({ - ...jest.requireActual('@deriv/components'), - ExpansionPanel: () =>
ExpansionPanel
, -})); - -describe('', () => { - it('should render PaymentAgentCard component with ExpansionPanel', () => { - render(); - - expect(screen.getByText('ExpansionPanel')).toBeInTheDocument(); - }); -}); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card.spec.tsx b/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card.spec.tsx new file mode 100644 index 000000000000..a5c486721ef4 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-card/__tests__/payment-agent-card.spec.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import PaymentAgentCard from '../payment-agent-card'; +import { TPaymentAgent } from '../../../../types'; + +jest.mock('@deriv/components', () => ({ + ...jest.requireActual('@deriv/components'), + ExpansionPanel: () =>
ExpansionPanel
, +})); + +describe('', () => { + it('should render PaymentAgentCard component with ExpansionPanel', () => { + const payment_agent: TPaymentAgent = { + currencies: 'USD', + deposit_commission: '', + email: '', + further_information: '', + max_withdrawal: '', + min_withdrawal: '', + name: '', + paymentagent_loginid: '', + phone_numbers: [], + summary: '', + supported_banks: [{ payment_method: '' }], + supported_payment_methods: [], + urls: [{ url: '' }], + withdrawal_commission: '', + }; + render(); + + expect(screen.getByText('ExpansionPanel')).toBeInTheDocument(); + }); +}); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/__tests__/heplers.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/__tests__/helpers.spec.tsx similarity index 98% rename from packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/__tests__/heplers.spec.js rename to packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/__tests__/helpers.spec.tsx index 1727876cccea..768e9ce7882b 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/__tests__/heplers.spec.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/__tests__/helpers.spec.tsx @@ -2,7 +2,7 @@ import Constants from 'Constants/constants'; import { getNormalizedPaymentMethod } from 'Utils/utility'; import { hasNormalizedPaymentMethods, getUniquePaymentAgentSupportedBanks } from '../helpers'; -describe('Heplers', () => { +describe('Helpers', () => { it('should normalize payment methods', () => { expect(getNormalizedPaymentMethod('E-WALLET', Constants.icon_payment_methods, true)).toBe('Ewallet'); expect(getNormalizedPaymentMethod('Bank Wire Transfer', Constants.icon_payment_methods, true)).toBe('Bank'); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/helpers.js b/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/helpers.js deleted file mode 100644 index e4bd474c9007..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/helpers.js +++ /dev/null @@ -1,18 +0,0 @@ -import Constants from 'Constants/constants'; -import { getNormalizedPaymentMethod } from 'Utils/utility'; - -export const hasNormalizedPaymentMethods = all_payment_methods => { - if (all_payment_methods.length > 0) { - return !all_payment_methods.every( - method => getNormalizedPaymentMethod(method.payment_method, Constants.icon_payment_methods, true) === '' - ); - } - return false; -}; - -export const getUniquePaymentAgentSupportedBanks = supported_banks => { - const normalized_payment_methods = supported_banks - .map(bank => getNormalizedPaymentMethod(bank.payment_method, Constants.icon_payment_methods, true)) - .filter(Boolean); - return [...new Set(normalized_payment_methods)]; -}; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/helpers.ts b/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/helpers.ts new file mode 100644 index 000000000000..b9501ca2605d --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/helpers.ts @@ -0,0 +1,21 @@ +import Constants from 'Constants/constants'; +import { getNormalizedPaymentMethod } from 'Utils/utility'; + +export const hasNormalizedPaymentMethods = (all_payment_methods?: { payment_method: string }[]) => { + if (all_payment_methods && all_payment_methods.length > 0) { + return !all_payment_methods.every( + (method: { payment_method: string }) => + getNormalizedPaymentMethod(method.payment_method, Constants.icon_payment_methods, true) === '' + ); + } + return false; +}; + +export const getUniquePaymentAgentSupportedBanks = (supported_banks?: { payment_method: string }[]) => { + const normalized_payment_methods = + supported_banks && + supported_banks + .map(bank => getNormalizedPaymentMethod(bank.payment_method, Constants.icon_payment_methods, true)) + .filter(Boolean); + return Array.from(new Set(normalized_payment_methods)); +}; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/index.ts similarity index 100% rename from packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/index.js rename to packages/cashier/src/pages/payment-agent/payment-agent-card/helpers/index.ts diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-card/index.js deleted file mode 100644 index 85c17942387f..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-card/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import PaymentAgentCard from './payment-agent-card.jsx'; - -export default PaymentAgentCard; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/index.ts b/packages/cashier/src/pages/payment-agent/payment-agent-card/index.ts new file mode 100644 index 000000000000..4791d1886681 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-card/index.ts @@ -0,0 +1,3 @@ +import PaymentAgentCard from './payment-agent-card'; + +export default PaymentAgentCard; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card-description.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card-description.tsx similarity index 91% rename from packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card-description.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card-description.tsx index b75f70119fde..1db06b173096 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card-description.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card-description.tsx @@ -1,12 +1,17 @@ -import PropTypes from 'prop-types'; import { toJS } from 'mobx'; import React from 'react'; import { Icon, Text } from '@deriv/components'; import { capitalizeFirstLetter } from '@deriv/shared'; import { hasNormalizedPaymentMethods, getUniquePaymentAgentSupportedBanks } from './helpers'; import PaymentAgentDetail from '../payment-agent-detail'; +import { TPaymentAgent } from '../../../types'; -const PaymentAgentCardDescription = ({ is_dark_mode_on, payment_agent }) => { +type TPaymentAgentCardDescription = { + is_dark_mode_on?: boolean; + payment_agent: TPaymentAgent; +}; + +const PaymentAgentCardDescription = ({ is_dark_mode_on, payment_agent }: TPaymentAgentCardDescription) => { const payment_agent_urls = toJS(payment_agent.urls); return ( @@ -49,9 +54,4 @@ const PaymentAgentCardDescription = ({ is_dark_mode_on, payment_agent }) => { ); }; -PaymentAgentCardDescription.propTypes = { - is_dark_mode_on: PropTypes.bool, - payment_agent: PropTypes.object, -}; - export default PaymentAgentCardDescription; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card.tsx similarity index 77% rename from packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card.tsx index c2349375318e..d86b7e63691d 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-card/payment-agent-card.tsx @@ -1,13 +1,19 @@ -import PropTypes from 'prop-types'; import classNames from 'classnames'; import React from 'react'; import { ExpansionPanel } from '@deriv/components'; -import PaymentAgentCardDescription from './payment-agent-card-description.jsx'; +import PaymentAgentCardDescription from './payment-agent-card-description'; import PaymentAgentDepositDetails from '../payment-agent-deposit-details'; import PaymentAgentListedWithdrawForm from '../payment-agent-listed-withdraw-form'; import './payment-agent-card.scss'; +import { TPaymentAgent } from '../../../types'; -const PaymentAgentCard = ({ is_dark_mode_on, is_deposit, payment_agent }) => { +type TPaymentAgentCard = { + is_dark_mode_on?: boolean; + is_deposit?: boolean; + payment_agent: TPaymentAgent; +}; + +const PaymentAgentCard = ({ is_dark_mode_on = false, is_deposit = false, payment_agent }: TPaymentAgentCard) => { const message = { header: , content: is_deposit ? ( @@ -27,10 +33,4 @@ const PaymentAgentCard = ({ is_dark_mode_on, is_deposit, payment_agent }) => { ); }; -PaymentAgentCard.propTypes = { - is_dark_mode_on: PropTypes.bool, - is_deposit: PropTypes.bool, - payment_agent: PropTypes.object, -}; - export default PaymentAgentCard; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-container/__tests__/payment-agent-container.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-container/__tests__/payment-agent-container.spec.tsx similarity index 88% rename from packages/cashier/src/pages/payment-agent/payment-agent-container/__tests__/payment-agent-container.spec.js rename to packages/cashier/src/pages/payment-agent/payment-agent-container/__tests__/payment-agent-container.spec.tsx index 082c9442b3e0..741768dc1774 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-container/__tests__/payment-agent-container.spec.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-container/__tests__/payment-agent-container.spec.tsx @@ -3,6 +3,7 @@ import { fireEvent, screen, render } from '@testing-library/react'; import PaymentAgentContainer from '../payment-agent-container'; import { isMobile } from '@deriv/shared'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), @@ -14,21 +15,23 @@ jest.mock('@deriv/components', () => ({ Loading: () =>
Loading
, })); -jest.mock('Pages/payment-agent/payment-agent-unlisted-withdraw-form', () => () => ( -
PaymentAgentUnlistedWithdrawForm
-)); -jest.mock('Pages/payment-agent/payment-agent-withdraw-confirm', () => () =>
PaymentAgentWithdrawConfirm
); -jest.mock('Pages/payment-agent/payment-agent-receipt', () => () =>
PaymentAgentReceipt
); -jest.mock('Pages/payment-agent/payment-agent-disclaimer', () => () =>
PaymentAgentDisclaimer
); -jest.mock('Pages/payment-agent/payment-agent-search-box', () => () =>
PaymentAgentSearchBox
); +jest.mock('Pages/payment-agent/payment-agent-unlisted-withdraw-form', () => + jest.fn(() =>
PaymentAgentUnlistedWithdrawForm
) +); +jest.mock('Pages/payment-agent/payment-agent-withdraw-confirm', () => + jest.fn(() =>
PaymentAgentWithdrawConfirm
) +); +jest.mock('Pages/payment-agent/payment-agent-receipt', () => jest.fn(() =>
PaymentAgentReceipt
)); +jest.mock('Pages/payment-agent/payment-agent-disclaimer', () => jest.fn(() =>
PaymentAgentDisclaimer
)); +jest.mock('Pages/payment-agent/payment-agent-search-box', () => jest.fn(() =>
PaymentAgentSearchBox
)); describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { - ui: { - app_contents_scroll_ref: { - current: {}, + mockRootStore = mockStore({ + client: { + verification_code: { + payment_agent_withdraw: 'ABCdef', }, }, modules: { @@ -50,7 +53,7 @@ describe('', () => { phones: '+12345678', supported_banks: [{ payment_method: 'Visa' }], telephone: '+12345678', - url: 'http://www.pa.com', + url: 'https://www.pa.com', withdrawal_commission: 0, }, { @@ -65,7 +68,7 @@ describe('', () => { phones: '+12345678', supported_banks: [{ payment_method: 'Visa' }, { payment_method: 'Mastercard' }], telephone: '+12345678', - url: 'http://www.pa.com', + url: 'https://www.pa.com', withdrawal_commission: 0, }, ], @@ -78,13 +81,13 @@ describe('', () => { }, }, }, - }; + }); }); const renderPaymentAgentContainer = (is_deposit = true) => { return render( - + ); }; @@ -139,7 +142,7 @@ describe('', () => { }); it('should show PaymentAgentDisclaimer in mobile view', () => { - isMobile.mockReturnValue(true); + (isMobile as jest.Mock).mockReturnValue(true); renderPaymentAgentContainer(); expect(screen.getByText('PaymentAgentDisclaimer')).toBeInTheDocument(); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-container/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-container/index.js deleted file mode 100644 index f9ed45aaa6ab..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-container/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import PaymentAgentContainer from './payment-agent-container.jsx'; - -export default PaymentAgentContainer; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-container/index.ts b/packages/cashier/src/pages/payment-agent/payment-agent-container/index.ts new file mode 100644 index 000000000000..ad07e8a4ad51 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-container/index.ts @@ -0,0 +1,3 @@ +import PaymentAgentContainer from './payment-agent-container'; + +export default PaymentAgentContainer; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-container/payment-agent-container.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-container/payment-agent-container.tsx similarity index 91% rename from packages/cashier/src/pages/payment-agent/payment-agent-container/payment-agent-container.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-container/payment-agent-container.tsx index 5e7afebb77d7..8550a56252d0 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-container/payment-agent-container.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-container/payment-agent-container.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import { DesktopWrapper, Dropdown, Icon, Loading, MobileWrapper, SelectNative, Text } from '@deriv/components'; import { useStore, observer } from '@deriv/stores'; @@ -14,6 +13,10 @@ import PaymentAgentWithdrawConfirm from '../payment-agent-withdraw-confirm'; import { useCashierStore } from '../../../stores/useCashierStores'; import './payment-agent-container.scss'; +type TPaymentAgentContainer = { + is_deposit?: boolean; +}; + const PaymentAgentSearchWarning = () => { return (
@@ -28,7 +31,7 @@ const PaymentAgentSearchWarning = () => { ); }; -const PaymentAgentContainer = observer(({ is_deposit, verification_code }) => { +const PaymentAgentContainer = observer(({ is_deposit }: TPaymentAgentContainer) => { const { ui } = useStore(); const { app_contents_scroll_ref, is_dark_mode_on } = ui; const { payment_agent: payment_agent_store } = useCashierStore(); @@ -59,7 +62,7 @@ const PaymentAgentContainer = observer(({ is_deposit, verification_code }) => { }, [onChangePaymentMethod]); React.useEffect(() => { - if (app_contents_scroll_ref) app_contents_scroll_ref.current.scrollTop = 0; + if (app_contents_scroll_ref && app_contents_scroll_ref.current) app_contents_scroll_ref.current.scrollTop = 0; // eslint-disable-next-line react-hooks/exhaustive-deps }, [is_try_withdraw_successful, is_withdraw_successful]); @@ -71,7 +74,7 @@ const PaymentAgentContainer = observer(({ is_deposit, verification_code }) => { ]; if (is_try_withdraw_successful) { - return ; + return ; } if (is_withdraw_successful) { @@ -79,12 +82,7 @@ const PaymentAgentContainer = observer(({ is_deposit, verification_code }) => { } if (is_unlisted_withdraw) { - return ( - - ); + return ; } return ( @@ -134,6 +132,7 @@ const PaymentAgentContainer = observer(({ is_deposit, verification_code }) => { { onChangePaymentMethod({ target: { name: 'payment_methods', - value: e.target.value ? e.target.value.toLowerCase() : 0, + value: e.target.value ? e.target.value.toLowerCase() : '0', }, }) } @@ -177,9 +176,4 @@ const PaymentAgentContainer = observer(({ is_deposit, verification_code }) => { ); }); -PaymentAgentContainer.propTypes = { - is_deposit: PropTypes.bool, - verification_code: PropTypes.string, -}; - export default PaymentAgentContainer; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/__tests__/payment-agent-card-deposit-details.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/__tests__/payment-agent-card-deposit-details.spec.tsx similarity index 84% rename from packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/__tests__/payment-agent-card-deposit-details.spec.js rename to packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/__tests__/payment-agent-card-deposit-details.spec.tsx index f38cc5104173..7f5fcc8a3061 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/__tests__/payment-agent-card-deposit-details.spec.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/__tests__/payment-agent-card-deposit-details.spec.tsx @@ -5,11 +5,18 @@ import PaymentAgentDepositDetails from '../payment-agent-deposit-details'; describe('', () => { const mocked_payment_agent = { currency: 'USD', + currencies: 'USD', deposit_commission: '10', email: 'pa@example.com', + further_information: '', max_withdrawal: '2000', min_withdrawal: '10', - phones: [{ phone_number: '+12345678' }, { phone_number: '+87654321' }], + name: '', + paymentagent_loginid: '', + phone_numbers: [{ phone_number: '+12345678' }, { phone_number: '+87654321' }], + summary: '', + supported_payment_methods: [], + urls: [], withdrawal_commission: '0', }; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/index.ts similarity index 88% rename from packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/index.js rename to packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/index.ts index 4e46d62b2950..2ac2ed2a98e1 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/index.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/index.ts @@ -1,3 +1,3 @@ -import PaymentAgentDepositDetails from './payment-agent-deposit-details.jsx'; +import PaymentAgentDepositDetails from './payment-agent-deposit-details'; export default PaymentAgentDepositDetails; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/payment-agent-deposit-details.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/payment-agent-deposit-details.tsx similarity index 82% rename from packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/payment-agent-deposit-details.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/payment-agent-deposit-details.tsx index 268169141f2e..bcda302a7daf 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/payment-agent-deposit-details.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-deposit-details/payment-agent-deposit-details.tsx @@ -1,18 +1,22 @@ -import PropTypes from 'prop-types'; import { toJS } from 'mobx'; import React from 'react'; import { Money } from '@deriv/components'; import { localize } from '@deriv/translations'; import PaymentAgentDetail from '../payment-agent-detail'; import './payment-agent-deposit-details.scss'; +import { TPaymentAgent } from '../../../types'; -const PaymentAgentDepositDetails = ({ payment_agent }) => { - const payment_agent_phones = toJS(payment_agent.phones); +type TPaymentAgentDepositDetails = { + payment_agent: TPaymentAgent; +}; + +const PaymentAgentDepositDetails = ({ payment_agent }: TPaymentAgentDepositDetails) => { + const payment_agent_phones = toJS(payment_agent.phone_numbers); const PaymentAgentPhonesDetails = () => { return ( - {payment_agent.phones.map(phone => phone.phone_number)} + {payment_agent.phone_numbers.map(phone => phone.phone_number)} ); }; @@ -35,7 +39,7 @@ const PaymentAgentDepositDetails = ({ payment_agent }) => { const PaymentAgentMinimumWithdrawalDetails = () => { return ( - + ); }; @@ -43,7 +47,7 @@ const PaymentAgentDepositDetails = ({ payment_agent }) => { const PaymentAgentMaximumWithdrawalDetails = () => { return ( - + ); }; @@ -84,8 +88,4 @@ const PaymentAgentDepositDetails = ({ payment_agent }) => { ); }; -PaymentAgentDepositDetails.propTypes = { - payment_agent: PropTypes.object, -}; - export default PaymentAgentDepositDetails; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-detail/__tests__/payment-agent-detail.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-detail/__tests__/payment-agent-detail.spec.tsx similarity index 94% rename from packages/cashier/src/pages/payment-agent/payment-agent-detail/__tests__/payment-agent-detail.spec.js rename to packages/cashier/src/pages/payment-agent/payment-agent-detail/__tests__/payment-agent-detail.spec.tsx index 349874261013..b4c610cca793 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-detail/__tests__/payment-agent-detail.spec.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-detail/__tests__/payment-agent-detail.spec.tsx @@ -20,7 +20,7 @@ describe('', () => { }); it('should show proper description if children is an array', () => { - render(); + render({['+12345678', '+87654321']}); expect(screen.getByText('+12345678,')).toBeInTheDocument(); expect(screen.getByText('+87654321')).toBeInTheDocument(); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-detail/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-detail/index.js deleted file mode 100644 index 775708aae302..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-detail/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import PaymentAgentDetail from './payment-agent-detail.jsx'; - -export default PaymentAgentDetail; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-detail/index.ts b/packages/cashier/src/pages/payment-agent/payment-agent-detail/index.ts new file mode 100644 index 000000000000..3fb1fb26e6ae --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-detail/index.ts @@ -0,0 +1,3 @@ +import PaymentAgentDetail from './payment-agent-detail'; + +export default PaymentAgentDetail; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-detail/payment-agent-detail.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-detail/payment-agent-detail.tsx similarity index 76% rename from packages/cashier/src/pages/payment-agent/payment-agent-detail/payment-agent-detail.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-detail/payment-agent-detail.tsx index 4a5e8a78114f..c45988ff428a 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-detail/payment-agent-detail.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-detail/payment-agent-detail.tsx @@ -1,17 +1,33 @@ -import PropTypes from 'prop-types'; import React from 'react'; import classNames from 'classnames'; import { Icon, Text } from '@deriv/components'; import './payment-agent-detail.scss'; -const PaymentAgentDetail = ({ action, children, className, has_red_color, icon, is_link, title, ...rest }) => { +type TPaymentAgentDetail = { + action?: string; + children?: React.ReactNode; + className?: string; + has_red_color?: boolean; + icon?: string; + is_link?: boolean; + title?: string; + rel?: string; + target?: string; +}; + +const PaymentAgentDetail = ({ + action, + children, + className, + has_red_color, + icon, + is_link, + title, + ...rest +}: TPaymentAgentDetail) => { const detail = Array.isArray(children) ? children : [children]; return ( -
+
{icon && (
@@ -30,7 +46,7 @@ const PaymentAgentDetail = ({ action, children, className, has_red_color, icon, as='a' color={has_red_color ? 'red' : 'prominent'} data-testid='dt_payment_agent_detail_link' - href={`${action ? `${action}:` : ''}${child}`} + href={(action ? `${action}:` : '') + child} line_height='s' size={!title ? 'xxs' : 'xs'} weight='bold' @@ -59,14 +75,4 @@ const PaymentAgentDetail = ({ action, children, className, has_red_color, icon, ); }; -PaymentAgentDetail.propTypes = { - action: PropTypes.string, - children: PropTypes.oneOfType([PropTypes.array, PropTypes.element, PropTypes.string]), - className: PropTypes.string, - has_red_color: PropTypes.bool, - icon: PropTypes.string, - is_link: PropTypes.bool, - title: PropTypes.string, -}; - export default PaymentAgentDetail; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-details/payment-agent-details.scss b/packages/cashier/src/pages/payment-agent/payment-agent-details/payment-agent-details.scss deleted file mode 100644 index 8c917b595517..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-details/payment-agent-details.scss +++ /dev/null @@ -1,44 +0,0 @@ -.payment-agent-details { - &__contact { - text-decoration: none; - color: var(--brand-red-coral); - font-weight: bold; - font-size: var(--text-size-xs); - word-wrap: break-word; - - @include mobile { - padding-left: 0; - } - } - &__accordion-content { - &-line { - margin-bottom: 8px; - display: flex; - flex-direction: row; - word-break: break-all; - - &:first-child { - & .payment-agent-details__contact { - color: var(--text-prominent); - } - } - &:last-child { - margin-bottom: 0; - } - @include mobile { - overflow: hidden; - text-overflow: ellipsis; - - &:not(:first-child) { - color: var(--brand-red-coral); - } - } - } - &-icon { - vertical-align: middle; - margin-right: 0.8rem; - /* postcss-bem-linter: ignore */ - --fill-color1: var(--text-general); - } - } -} diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/index.js deleted file mode 100644 index ef8d732d143f..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import PaymentAgentDisclaimer from './payment-agent-disclaimer.jsx'; - -export default PaymentAgentDisclaimer; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/index.ts b/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/index.ts new file mode 100644 index 000000000000..bb52fcc40d95 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/index.ts @@ -0,0 +1,3 @@ +import PaymentAgentDisclaimer from './payment-agent-disclaimer'; + +export default PaymentAgentDisclaimer; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/payment-agent-disclaimer.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/payment-agent-disclaimer.tsx similarity index 90% rename from packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/payment-agent-disclaimer.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/payment-agent-disclaimer.tsx index 2a4f68b0be47..9a5251ff9061 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/payment-agent-disclaimer.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-disclaimer/payment-agent-disclaimer.tsx @@ -7,10 +7,10 @@ import './payment-agent-disclaimer.scss'; const PaymentAgentDisclaimer = () => { return (
- + - + { if (verify.error && 'code' in verify.error) return ; if (!verify.is_loading && verify.has_been_sent) return ; - if (verification_code || payment_agent.is_withdraw) - return ; + if (verification_code || payment_agent.is_withdraw) return ; return null; }); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/__tests__/payment-agent-listed-withdraw-form.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/__tests__/payment-agent-listed-withdraw-form.spec.tsx similarity index 79% rename from packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/__tests__/payment-agent-listed-withdraw-form.spec.js rename to packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/__tests__/payment-agent-listed-withdraw-form.spec.tsx index cdb24cb0279b..d99c46eccf2b 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/__tests__/payment-agent-listed-withdraw-form.spec.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/__tests__/payment-agent-listed-withdraw-form.spec.tsx @@ -4,6 +4,8 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import PaymentAgentListedWithdrawForm from '../payment-agent-listed-withdraw-form'; import { validNumber } from '@deriv/shared'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; +import { TPaymentAgent } from '../../../../types'; jest.mock('@deriv/components', () => ({ ...jest.requireActual('@deriv/components'), @@ -16,20 +18,20 @@ jest.mock('@deriv/shared/src/utils/validation/declarative-validation-rules', () })); describe('', () => { - let mockRootStore, payment_agent; + let mockRootStore: ReturnType, payment_agent: TPaymentAgent; beforeAll(() => { ReactDOM.createPortal = jest.fn(component => { - return component; + return component as React.ReactPortal; }); }); afterAll(() => { - ReactDOM.createPortal.mockClear(); + (ReactDOM.createPortal as jest.Mock).mockClear(); }); beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ ui: { is_dark_mode_on: false, toggleAccountsDialog: jest.fn(), @@ -56,8 +58,8 @@ describe('', () => { phone: [{ phone_number: '+12345678' }], text: 'Payment Agent of CR90000102 (Created from Script)', url: [ - { url: 'http://www.MyPAMyAdventure.com/' }, - { url: 'http://www.MyPAMyAdventure2.com/' }, + { url: 'https://www.MyPAMyAdventure.com/' }, + { url: 'https://www.MyPAMyAdventure2.com/' }, ], value: 'CR90000102', }, @@ -68,8 +70,8 @@ describe('', () => { phone: [{ phone_number: '+1234567822' }], text: 'Payment Agent of CR90000100 (Created from Script)', url: [ - { url: 'http://www.MyPAMyAdventure1.com/' }, - { url: 'http://www.MyPAMyAdventure2.com/' }, + { url: 'https://www.MyPAMyAdventure1.com/' }, + { url: 'https://www.MyPAMyAdventure2.com/' }, ], value: 'CR90000100', }, @@ -78,10 +80,11 @@ describe('', () => { }, }, }, - }; + }); payment_agent = { currency: 'USD', + currencies: 'USD', deposit_commission: '0', email: 'MyPaScript@example.com', further_information: 'Test Info', @@ -89,24 +92,25 @@ describe('', () => { min_withdrawal: '10', name: 'Payment Agent of CR90000102 (Created from Script)', paymentagent_loginid: 'CR90000102', - phones: [{ phone_number: '+12345678' }], + phone_numbers: [{ phone_number: '+12345678' }], + summary: '', supported_banks: [{ payment_method: 'MasterCard' }, { payment_method: 'Visa' }], - urls: [{ url: 'http://www.MyPAMyAdventure.com/' }, { url: 'http://www.MyPAMyAdventure2.com/' }], + supported_payment_methods: [], + urls: [{ url: 'https://www.MyPAMyAdventure.com/' }, { url: 'https://www.MyPAMyAdventure2.com/' }], withdrawal_commission: '0', }; }); - const renderPaymentAgentListedWithdrawForm = (is_rerender = false) => { - const ui = ( + const mockPaymentAgentListedWithdrawForm = () => { + return ( ); - return is_rerender ? ui : render(ui); }; it('should render the component', () => { - renderPaymentAgentListedWithdrawForm(); + render(mockPaymentAgentListedWithdrawForm()); expect(screen.getByText('Withdrawal amount')).toBeInTheDocument(); expect(screen.getByText('USD')).toBeInTheDocument(); @@ -119,20 +123,20 @@ describe('', () => { it('should show loader when is_loading equal to true or there is no payment agents', () => { mockRootStore.modules.cashier.general_store.is_loading = true; - const { rerender } = renderPaymentAgentListedWithdrawForm(); + const { rerender } = render(mockPaymentAgentListedWithdrawForm()); expect(screen.getByText('Loading')).toBeInTheDocument(); mockRootStore.modules.cashier.payment_agent.agents = []; - rerender(renderPaymentAgentListedWithdrawForm(true)); + rerender(mockPaymentAgentListedWithdrawForm()); expect(screen.getByText('Loading')).toBeInTheDocument(); }); it('should show error message, if amount is not valid', async () => { - validNumber.mockReturnValue({ is_ok: false, message: 'error_message' }); - renderPaymentAgentListedWithdrawForm(); + (validNumber as jest.Mock).mockReturnValue({ is_ok: false, message: 'error_message' }); + render(mockPaymentAgentListedWithdrawForm()); const el_input_amount = screen.getByLabelText('Enter amount'); const el_continue_btn = screen.getByRole('button', { name: 'Continue' }); @@ -142,11 +146,11 @@ describe('', () => { await waitFor(() => { expect(screen.getByText('error_message')).toBeInTheDocument(); }); - validNumber.mockReturnValue({ is_ok: true, message: '' }); + (validNumber as jest.Mock).mockReturnValue({ is_ok: true, message: '' }); }); it('should show Insufficient balance error', async () => { - renderPaymentAgentListedWithdrawForm(); + render(mockPaymentAgentListedWithdrawForm()); const el_input_amount = screen.getByLabelText('Enter amount'); const el_continue_btn = screen.getByRole('button', { name: 'Continue' }); @@ -159,7 +163,7 @@ describe('', () => { }); it('should trigger requestTryPaymentAgentWithdraw, when all data are valid', async () => { - renderPaymentAgentListedWithdrawForm(); + render(mockPaymentAgentListedWithdrawForm()); const el_input_amount = screen.getByLabelText('Enter amount'); const el_continue_btn = screen.getByRole('button', { name: 'Continue' }); @@ -170,7 +174,7 @@ describe('', () => { expect(mockRootStore.modules.cashier.payment_agent.requestTryPaymentAgentWithdraw).toHaveBeenCalledWith({ loginid: 'CR90000102', currency: 'USD', - amount: '100', + amount: 100, verification_code: 'ABCdef', }); }); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/index.ts similarity index 82% rename from packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/index.js rename to packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/index.ts index 2f9099cba0a4..0435549cf395 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/index.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/index.ts @@ -1,3 +1,3 @@ -import PaymentAgentListedWithdrawForm from './payment-agent-listed-withdraw-form.jsx'; +import PaymentAgentListedWithdrawForm from './payment-agent-listed-withdraw-form'; export default PaymentAgentListedWithdrawForm; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/payment-agent-listed-withdraw-form.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/payment-agent-listed-withdraw-form.tsx similarity index 80% rename from packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/payment-agent-listed-withdraw-form.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/payment-agent-listed-withdraw-form.tsx index 040be9f20fed..79869b256145 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/payment-agent-listed-withdraw-form.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-listed-withdraw-form/payment-agent-listed-withdraw-form.tsx @@ -1,7 +1,6 @@ import classNames from 'classnames'; -import PropTypes from 'prop-types'; import React from 'react'; -import { Field, Formik, Form } from 'formik'; +import { Field, FieldProps, Formik, Form } from 'formik'; import { Button, Input, Loading, Money, Text } from '@deriv/components'; import { getDecimalPlaces, getCurrencyDisplayCode, validNumber } from '@deriv/shared'; import { localize, Localize } from '@deriv/translations'; @@ -9,31 +8,49 @@ import { observer, useStore } from '@deriv/stores'; import ErrorDialog from 'Components/error-dialog'; import { useCashierStore } from '../../../stores/useCashierStores'; import './payment-agent-listed-withdraw-form.scss'; +import { TPaymentAgent } from '../../../types'; -const validateWithdrawal = (values, { balance, currency, payment_agent = {} }) => { - const errors = {}; +type TValidateWithdrawalValueProps = { + amount: string; +}; + +type TValidateWithdrawalProps = { + balance: string; + currency: string; + payment_agent: TPaymentAgent; +}; + +type TPaymentAgentListedWithdrawForm = { + payment_agent: TPaymentAgent; +}; + +const validateWithdrawal = ( + values: TValidateWithdrawalValueProps, + { balance, currency, payment_agent }: TValidateWithdrawalProps +) => { + const errors: { amount?: string } = {}; const { is_ok, message } = validNumber(values.amount, { type: 'float', decimals: getDecimalPlaces(currency), ...(payment_agent.min_withdrawal && { - min: payment_agent.min_withdrawal, - max: payment_agent.max_withdrawal, + min: Number(payment_agent.min_withdrawal), + max: payment_agent.max_withdrawal ? Number(payment_agent.max_withdrawal) : undefined, }), }); if (!values.amount) { errors.amount = localize('This field is required.'); - } else if (!is_ok) { + } else if (!is_ok && message) { errors.amount = message; - } else if (+balance < +values.amount) { + } else if (Number(balance) < Number(values.amount)) { errors.amount = localize('Insufficient balance.'); } return errors; }; -const PaymentAgentListedWithdrawForm = observer(({ payment_agent }) => { +const PaymentAgentListedWithdrawForm = observer(({ payment_agent }: TPaymentAgentListedWithdrawForm) => { const { client } = useStore(); const { general_store, payment_agent: payment_agent_store } = useCashierStore(); const { @@ -54,31 +71,28 @@ const PaymentAgentListedWithdrawForm = observer(({ payment_agent }) => { onMount(); }, [onMount]); - const input_ref = React.useRef(null); + const input_ref = React.useRef(null); React.useEffect(() => { if (input_ref.current) { - input_ref.current.value = null; + input_ref.current.value = ''; } }, [selected_bank]); - const validateWithdrawalPassthrough = values => + const validateWithdrawalPassthrough = (values: TValidateWithdrawalValueProps) => validateWithdrawal(values, { balance, currency, payment_agent: payment_agent_list.find(pa => pa.value === payment_agent.paymentagent_loginid), - }); + } as TValidateWithdrawalProps); - const onWithdrawalPassthrough = async (values, actions) => { - const payment_agent_withdraw = await requestTryPaymentAgentWithdraw({ + const onWithdrawalPassthrough = async (values: TValidateWithdrawalValueProps) => { + await requestTryPaymentAgentWithdraw({ loginid: payment_agent.paymentagent_loginid, currency, - amount: values.amount, + amount: Number(values.amount), verification_code, }); - if (payment_agent_withdraw?.error) { - actions.setSubmitting(false); - } }; if (is_loading || !payment_agent_list.length) { @@ -109,7 +123,7 @@ const PaymentAgentListedWithdrawForm = observer(({ payment_agent }) => { amount={ payment_agent_list.find( pa => pa.value === payment_agent.paymentagent_loginid - ).min_withdrawal + )?.min_withdrawal || '' } currency={payment_agent.currency} show_currency @@ -119,7 +133,7 @@ const PaymentAgentListedWithdrawForm = observer(({ payment_agent }) => { amount={ payment_agent_list.find( pa => pa.value === payment_agent.paymentagent_loginid - ).max_withdrawal + )?.max_withdrawal || '' } currency={payment_agent.currency} show_currency @@ -132,7 +146,7 @@ const PaymentAgentListedWithdrawForm = observer(({ payment_agent }) => { return ( - {({ field }) => ( + {({ field }: FieldProps) => ( { })} type='text' label={localize('Enter amount')} - error={touched.amount && errors.amount} + error={(touched.amount && errors.amount) || ''} required autoComplete='off' maxLength={30} @@ -173,8 +187,4 @@ const PaymentAgentListedWithdrawForm = observer(({ payment_agent }) => { ); }); -PaymentAgentListedWithdrawForm.propTypes = { - payment_agent: PropTypes.object, -}; - export default PaymentAgentListedWithdrawForm; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-receipt/__tests__/payment-agent-receipt.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-receipt/__tests__/payment-agent-receipt.spec.tsx similarity index 92% rename from packages/cashier/src/pages/payment-agent/payment-agent-receipt/__tests__/payment-agent-receipt.spec.js rename to packages/cashier/src/pages/payment-agent/payment-agent-receipt/__tests__/payment-agent-receipt.spec.tsx index c4b0a7880d11..bd5aea2c84c6 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-receipt/__tests__/payment-agent-receipt.spec.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-receipt/__tests__/payment-agent-receipt.spec.tsx @@ -1,23 +1,24 @@ import React from 'react'; import PaymentAgentReceipt from '../payment-agent-receipt'; import { fireEvent, render, screen } from '@testing-library/react'; -import { createBrowserHistory } from 'history'; +import { BrowserHistory, createBrowserHistory } from 'history'; import { Router } from 'react-router'; import { isMobile, routes } from '@deriv/shared'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), isMobile: jest.fn(() => false), })); -jest.mock('Pages/payment-agent/payment-agent-disclaimer', () => () =>
PaymentAgentDisclaimer
); +jest.mock('Pages/payment-agent/payment-agent-disclaimer', () => jest.fn(() => 'PaymentAgentDisclaimer')); describe('', () => { - let history, mockRootStore; + let history: BrowserHistory, mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { currency: 'USD', }, @@ -39,7 +40,7 @@ describe('', () => { }, }, }, - }; + }); history = createBrowserHistory(); }); @@ -99,7 +100,7 @@ describe('', () => { }); it('should show PaymentAgentDisclaimer in mobile view', () => { - isMobile.mockReturnValue(true); + (isMobile as jest.Mock).mockReturnValue(true); renderPaymentAgentReceipt(); expect(screen.getByText('PaymentAgentDisclaimer')).toBeInTheDocument(); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-receipt/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-receipt/index.js deleted file mode 100644 index a6b7e80ff2f4..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-receipt/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import PaymentAgentReceipt from './payment-agent-receipt.jsx'; - -export default PaymentAgentReceipt; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-receipt/index.ts b/packages/cashier/src/pages/payment-agent/payment-agent-receipt/index.ts new file mode 100644 index 000000000000..c4f35ee1bf40 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-receipt/index.ts @@ -0,0 +1,3 @@ +import PaymentAgentReceipt from './payment-agent-receipt'; + +export default PaymentAgentReceipt; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-receipt/payment-agent-receipt.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-receipt/payment-agent-receipt.tsx similarity index 82% rename from packages/cashier/src/pages/payment-agent/payment-agent-receipt/payment-agent-receipt.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-receipt/payment-agent-receipt.tsx index 04416f9b9eea..c9c6b3ad6fb6 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-receipt/payment-agent-receipt.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-receipt/payment-agent-receipt.tsx @@ -1,5 +1,4 @@ import classNames from 'classnames'; -import PropTypes from 'prop-types'; import React from 'react'; import { withRouter } from 'react-router'; import { Button, Text } from '@deriv/components'; @@ -11,35 +10,43 @@ import PaymentAgentDisclaimer from '../payment-agent-disclaimer'; import SideNote from 'Components/side-note'; import { useCashierStore } from '../../../stores/useCashierStores'; import './payment-agent-receipt.scss'; +import { BrowserHistory } from 'history'; +import { TPaymentAgent } from '../../../types'; -const openStatement = (history, resetPaymentAgent) => { +const openStatement = (history: BrowserHistory, resetPaymentAgent: VoidFunction) => { history.push(routes.statement); resetPaymentAgent(); }; -const PaymentAgentDetails = ({ payment_agent_email, payment_agent_phones, payment_agent_urls }) => { +type TPaymentAgentDetails = Pick; + +type TPaymentAgentReceipt = { + history: BrowserHistory; +}; + +const PaymentAgentDetails = ({ email, phone_numbers, urls }: TPaymentAgentDetails) => { return (
- {payment_agent_phones && ( + {phone_numbers && ( - {payment_agent_phones.map(phone => phone.phone_number)} + {phone_numbers.map(phone => phone.phone_number)} )} - {payment_agent_email && ( + {email && ( - {payment_agent_email} + {email} )} - {payment_agent_urls && ( + {urls && ( - {payment_agent_urls.map(url => url.url)} + {urls.map(url => url.url)} )}
); }; -const PaymentAgentReceipt = observer(({ history }) => { +const PaymentAgentReceipt = observer(({ history }: TPaymentAgentReceipt) => { const { client, common } = useStore(); const { payment_agent: payment_agent_store } = useCashierStore(); const { currency } = client; @@ -52,7 +59,7 @@ const PaymentAgentReceipt = observer(({ history }) => { return (
- + { options={{ interpolation: { escapeValue: false } }} /> - + {receipt.payment_agent_email && receipt.payment_agent_phone && receipt.payment_agent_url && ( + + )}
)}
@@ -137,8 +146,4 @@ const PaymentAgentReceipt = observer(({ history }) => { ); }); -PaymentAgentReceipt.propTypes = { - history: PropTypes.object, -}; - export default withRouter(PaymentAgentReceipt); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-search-box/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-search-box/index.js deleted file mode 100644 index d623b7fa518e..000000000000 --- a/packages/cashier/src/pages/payment-agent/payment-agent-search-box/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import PaymentAgentSearchBox from './payment-agent-search-box.jsx'; - -export default PaymentAgentSearchBox; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-search-box/index.ts b/packages/cashier/src/pages/payment-agent/payment-agent-search-box/index.ts new file mode 100644 index 000000000000..e2fd8bd15c75 --- /dev/null +++ b/packages/cashier/src/pages/payment-agent/payment-agent-search-box/index.ts @@ -0,0 +1,3 @@ +import PaymentAgentSearchBox from './payment-agent-search-box'; + +export default PaymentAgentSearchBox; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-search-box/payment-agent-search-box.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-search-box/payment-agent-search-box.tsx similarity index 96% rename from packages/cashier/src/pages/payment-agent/payment-agent-search-box/payment-agent-search-box.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-search-box/payment-agent-search-box.tsx index 57e8ade75164..a86b999f5c38 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-search-box/payment-agent-search-box.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-search-box/payment-agent-search-box.tsx @@ -18,7 +18,7 @@ const PaymentAgentSearchBox = observer(() => { filterPaymentAgentList(); }; - const onSearch = search => { + const onSearch = (search: string) => { setSearchTerm(search.trim()); debouncedFunction(); }; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/__tests__/payment-agent-withdraw-form.spec.js b/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/__tests__/payment-agent-withdraw-form.spec.tsx similarity index 78% rename from packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/__tests__/payment-agent-withdraw-form.spec.js rename to packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/__tests__/payment-agent-withdraw-form.spec.tsx index c92487d18e58..b946c29f744d 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/__tests__/payment-agent-withdraw-form.spec.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/__tests__/payment-agent-withdraw-form.spec.tsx @@ -4,6 +4,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import PaymentAgentUnlistedWithdrawForm from '../payment-agent-unlisted-withdraw-form'; import { isMobile, validNumber } from '@deriv/shared'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), @@ -11,56 +12,55 @@ jest.mock('@deriv/shared', () => ({ isMobile: jest.fn(() => false), })); -jest.mock('Pages/payment-agent/payment-agent-disclaimer', () => () =>
PaymentAgentDisclaimer
); +jest.mock('Pages/payment-agent/payment-agent-disclaimer', () => jest.fn(() => 'PaymentAgentDisclaimer')); describe('', () => { - let mockRootStore, setIsUnlistedWithdraw; + let mockRootStore: ReturnType, setIsUnlistedWithdraw: (value: boolean) => void; beforeAll(() => { ReactDOM.createPortal = jest.fn(component => { - return component; + return component as React.ReactPortal; }); }); afterAll(() => { - ReactDOM.createPortal.mockClear(); + (ReactDOM.createPortal as jest.Mock).mockClear(); }); beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ ui: { disableApp: jest.fn(), enableApp: jest.fn() }, client: { balance: '1000', currency: 'USD', + verification_code: { + payment_agent_withdraw: 'ABCdef', + }, }, modules: { cashier: { payment_agent: { error: {}, onMountPaymentAgentWithdraw: jest.fn(), - requestTryPaymentAgentWithdraw: jest.fn().mockResolvedValue(), + requestTryPaymentAgentWithdraw: jest.fn().mockResolvedValue(''), }, }, }, - }; + }); setIsUnlistedWithdraw = jest.fn(); }); - const renderPaymentAgentUnlistedWithdrawForm = (is_rerender = false) => { - const ui = ( + const mockPaymentAgentUnlistedWithdrawForm = () => { + return ( - + ); - return is_rerender ? ui : render(ui); }; it('should render the component', () => { - renderPaymentAgentUnlistedWithdrawForm(); + render(mockPaymentAgentUnlistedWithdrawForm()); expect(screen.getByTestId('dt-back-arrow-icon')).toBeInTheDocument(); expect(screen.getByText('Back to list')).toBeInTheDocument(); @@ -73,7 +73,7 @@ describe('', () => { }); it('should trigger onclick callback when arrow back button was clicked', () => { - renderPaymentAgentUnlistedWithdrawForm(); + render(mockPaymentAgentUnlistedWithdrawForm()); const el_back_arrow_icon = screen.getByTestId('dt-back-arrow-icon'); fireEvent.click(el_back_arrow_icon); @@ -82,8 +82,8 @@ describe('', () => { }); it('should show different error messages', async () => { - validNumber.mockReturnValue({ is_ok: false, message: 'error_message' }); - const { rerender } = renderPaymentAgentUnlistedWithdrawForm(); + (validNumber as jest.Mock).mockReturnValue({ is_ok: false, message: 'error_message' }); + const { rerender } = render(mockPaymentAgentUnlistedWithdrawForm()); const el_input_account_number = screen.getByLabelText('Enter the payment agent account number'); const el_input_amount = screen.getByLabelText('Enter amount'); @@ -94,9 +94,9 @@ describe('', () => { await waitFor(() => { expect(screen.getByText('error_message')).toBeInTheDocument(); }); - validNumber.mockReturnValue({ is_ok: true, message: '' }); + (validNumber as jest.Mock).mockReturnValue({ is_ok: true, message: '' }); - rerender(renderPaymentAgentUnlistedWithdrawForm(true)); + rerender(mockPaymentAgentUnlistedWithdrawForm()); fireEvent.change(el_input_account_number, { target: { value: 'CR56656565' } }); fireEvent.change(el_input_amount, { target: { value: '2000' } }); fireEvent.click(el_continue_btn); @@ -104,7 +104,7 @@ describe('', () => { expect(screen.getByText('Insufficient balance.')).toBeInTheDocument(); }); - rerender(renderPaymentAgentUnlistedWithdrawForm(true)); + rerender(mockPaymentAgentUnlistedWithdrawForm()); fireEvent.change(el_input_account_number, { target: { value: '667766767' } }); fireEvent.change(el_input_amount, { target: { value: '100' } }); fireEvent.click(el_continue_btn); @@ -114,7 +114,7 @@ describe('', () => { }); it('should trigger requestTryPaymentAgentWithdraw, when all data are valid', async () => { - renderPaymentAgentUnlistedWithdrawForm(); + render(mockPaymentAgentUnlistedWithdrawForm()); const el_input_account_number = screen.getByLabelText('Enter the payment agent account number'); const el_input_amount = screen.getByLabelText('Enter amount'); @@ -127,15 +127,15 @@ describe('', () => { expect(mockRootStore.modules.cashier.payment_agent.requestTryPaymentAgentWithdraw).toHaveBeenCalledWith({ loginid: 'CR90000100', currency: 'USD', - amount: '100', + amount: 100, verification_code: 'ABCdef', }); }); }); it('should show PaymentAgentDisclaimer in mobile view', () => { - isMobile.mockReturnValue(true); - renderPaymentAgentUnlistedWithdrawForm(); + (isMobile as jest.Mock).mockReturnValue(true); + render(mockPaymentAgentUnlistedWithdrawForm()); expect(screen.getByText('PaymentAgentDisclaimer')).toBeInTheDocument(); }); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/index.ts similarity index 80% rename from packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/index.js rename to packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/index.ts index 59ceac399294..fa83bbf0d701 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/index.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/index.ts @@ -1,3 +1,3 @@ -import PaymentAgentUnlistedWithdrawForm from './payment-agent-unlisted-withdraw-form.jsx'; +import PaymentAgentUnlistedWithdrawForm from './payment-agent-unlisted-withdraw-form'; export default PaymentAgentUnlistedWithdrawForm; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/payment-agent-unlisted-withdraw-form.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/payment-agent-unlisted-withdraw-form.tsx similarity index 81% rename from packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/payment-agent-unlisted-withdraw-form.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/payment-agent-unlisted-withdraw-form.tsx index 07438a7d8c03..f9fe27c3624a 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/payment-agent-unlisted-withdraw-form.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-unlisted-withdraw-form/payment-agent-unlisted-withdraw-form.tsx @@ -1,7 +1,6 @@ import classNames from 'classnames'; -import PropTypes from 'prop-types'; import React from 'react'; -import { Field, Formik, Form } from 'formik'; +import { Field, FieldProps, Formik, Form } from 'formik'; import { Button, Icon, Input, Text } from '@deriv/components'; import { getDecimalPlaces, getCurrencyDisplayCode, validNumber, website_name } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; @@ -10,10 +9,26 @@ import PaymentAgentDisclaimer from '../payment-agent-disclaimer'; import ErrorDialog from 'Components/error-dialog'; import SideNote from 'Components/side-note'; import { useCashierStore } from '../../../stores/useCashierStores'; +import { TPaymentAgent } from '../../../types'; import './payment-agent-unlisted-withdraw-form.scss'; -const validateWithdrawal = (values, { balance, currency }) => { - const errors = {}; +type TValidateWithdrawalValueProps = { + amount: string; + account_number: string; +}; + +type TValidateWithdrawalProps = { + balance?: string | number; + currency: string; + payment_agent?: TPaymentAgent; +}; + +type TPaymentAgentUnlistedWithdrawForm = { + setIsUnlistedWithdraw: (value: boolean) => void; +}; + +const validateWithdrawal = (values: TValidateWithdrawalValueProps, { balance, currency }: TValidateWithdrawalProps) => { + const errors: { account_number?: string; amount?: string } = {}; const { is_ok, message } = validNumber(values.amount, { type: 'float', @@ -22,9 +37,9 @@ const validateWithdrawal = (values, { balance, currency }) => { if (!values.amount) { errors.amount = localize('This field is required.'); - } else if (!is_ok) { + } else if (!is_ok && message) { errors.amount = message; - } else if (+balance < +values.amount) { + } else if (balance !== undefined && Number(balance) < Number(values.amount)) { errors.amount = localize('Insufficient balance.'); } else if (!values.account_number) { errors.account_number = localize('This field is required.'); @@ -36,9 +51,10 @@ const validateWithdrawal = (values, { balance, currency }) => { return errors; }; -const PaymentAgentUnlistedWithdrawForm = observer(({ verification_code, setIsUnlistedWithdraw }) => { +const PaymentAgentUnlistedWithdrawForm = observer(({ setIsUnlistedWithdraw }: TPaymentAgentUnlistedWithdrawForm) => { const { client } = useStore(); const { balance, currency } = client; + const verification_code = client.verification_code.payment_agent_withdraw; const { payment_agent } = useCashierStore(); const { error, onMountPaymentAgentWithdraw: onMount, requestTryPaymentAgentWithdraw } = payment_agent; @@ -46,18 +62,16 @@ const PaymentAgentUnlistedWithdrawForm = observer(({ verification_code, setIsUnl onMount(); }, [onMount]); - const validateWithdrawalPassthrough = values => validateWithdrawal(values, { balance, currency }); + const validateWithdrawalPassthrough = (values: TValidateWithdrawalValueProps) => + validateWithdrawal(values, { balance, currency }); - const onWithdrawalPassthrough = async (values, actions) => { - const payment_agent_withdraw = await requestTryPaymentAgentWithdraw({ + const onWithdrawalPassthrough = async (values: TValidateWithdrawalValueProps) => { + await requestTryPaymentAgentWithdraw({ loginid: values.account_number, currency, - amount: values.amount, + amount: Number(values.amount), verification_code, }); - if (payment_agent_withdraw?.error) { - actions.setSubmitting(false); - } }; return ( @@ -87,13 +101,13 @@ const PaymentAgentUnlistedWithdrawForm = observer(({ verification_code, setIsUnl return ( - {({ field }) => ( + {({ field }: FieldProps) => (
- {({ field }) => ( + {({ field }: FieldProps) => ( ', () => { - let mockRootStore, verification_code; + let mockRootStore: ReturnType; beforeAll(() => { ReactDOM.createPortal = jest.fn(component => { - return component; + return component as React.ReactPortal; }); }); afterAll(() => { - ReactDOM.createPortal.mockClear(); + (ReactDOM.createPortal as jest.Mock).mockClear(); }); beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ ui: { disableApp: jest.fn(), enableApp: jest.fn() }, - client: { loginid: 'CR90000100' }, + client: { + loginid: 'CR90000100', + verification_code: { + payment_agent_withdraw: 'ABCdef', + }, + }, modules: { cashier: { payment_agent: { @@ -36,15 +42,13 @@ describe('', () => { }, }, }, - }; - - verification_code = 'ABCdef'; + }); }); const renderPaymentAgentWithdrawConfirm = () => { return render( - + ); }; @@ -109,6 +113,7 @@ describe('', () => { fireEvent.click(transfer_now_btn); const { loginid, currency, amount } = mockRootStore.modules.cashier.payment_agent.confirm; + const verification_code = mockRootStore.client.verification_code.payment_agent_withdraw; expect(mockRootStore.modules.cashier.payment_agent.requestPaymentAgentWithdraw).toHaveBeenCalledWith({ loginid, diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/index.ts similarity index 87% rename from packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/index.js rename to packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/index.ts index 63073af9865a..d56d754696d4 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/index.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/index.ts @@ -1,3 +1,3 @@ -import PaymentAgentWithdrawConfirm from './payment-agent-withdraw-confirm.jsx'; +import PaymentAgentWithdrawConfirm from './payment-agent-withdraw-confirm'; export default PaymentAgentWithdrawConfirm; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/payment-agent-withdraw-confirm.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/payment-agent-withdraw-confirm.tsx similarity index 78% rename from packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/payment-agent-withdraw-confirm.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/payment-agent-withdraw-confirm.tsx index 87f11c5aa6fb..2918232f0f88 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/payment-agent-withdraw-confirm.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-withdraw-confirm/payment-agent-withdraw-confirm.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import { Money } from '@deriv/components'; import { observer, useStore } from '@deriv/stores'; @@ -6,9 +5,9 @@ import { localize } from '@deriv/translations'; import TransferConfirm from 'Components/transfer-confirm'; import { useCashierStore } from '../../../stores/useCashierStores'; -const PaymentAgentWithdrawConfirm = observer(({ verification_code }) => { +const PaymentAgentWithdrawConfirm = observer(() => { const { client } = useStore(); - const { loginid: client_loginid } = client; + const { loginid: client_loginid, verification_code } = client; const { payment_agent } = useCashierStore(); const { confirm: { amount, currency, loginid, payment_agent_name }, @@ -20,7 +19,7 @@ const PaymentAgentWithdrawConfirm = observer(({ verification_code }) => { return ( { setIsTryWithdrawSuccessful(false); }} onClickConfirm={() => { - requestPaymentAgentWithdraw({ loginid, currency, amount, verification_code }); + requestPaymentAgentWithdraw({ + loginid, + currency, + amount, + verification_code: verification_code.payment_agent_withdraw, + }); }} /> ); }); -PaymentAgentWithdrawConfirm.propTypes = { - verification_code: PropTypes.string, -}; - export default PaymentAgentWithdrawConfirm; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/index.js b/packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/index.ts similarity index 85% rename from packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/index.js rename to packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/index.ts index ec51e9d04b27..54cc472f4715 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/index.js +++ b/packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/index.ts @@ -1,3 +1,3 @@ -import PaymentAgentWithdrawalLocked from './payment-agent-withdrawal-locked.jsx'; +import PaymentAgentWithdrawalLocked from './payment-agent-withdrawal-locked'; export default PaymentAgentWithdrawalLocked; diff --git a/packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/payment-agent-withdrawal-locked.jsx b/packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/payment-agent-withdrawal-locked.tsx similarity index 78% rename from packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/payment-agent-withdrawal-locked.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/payment-agent-withdrawal-locked.tsx index 33a6e66b3b8f..c383ccafd533 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/payment-agent-withdrawal-locked.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent-withdrawal-locked/payment-agent-withdrawal-locked.tsx @@ -1,13 +1,30 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Button, StaticUrl, Text } from '@deriv/components'; import { localize, Localize } from '@deriv/translations'; import { routes } from '@deriv/shared'; import { withRouter } from 'react-router-dom'; import Error from 'Components/error'; import './payment-agent-withdrawal-locked.scss'; +import { RouteComponentProps } from 'react-router'; +import { TServerError } from '../../../types'; -const PaymentAgentWithdrawalLockedItem = ({ item }) => { +type TPaymentAgentWithdrawalLockedItemProps = { + item: { + btn_confirm_text: string; + content: string | JSX.Element; + onConfirm: VoidFunction; + title?: string; + }; +}; + +type TPaymentAgentWithdrawalLockedProps = RouteComponentProps & { + error: TServerError & { + onClickButton?: VoidFunction; + setErrorMessage?: (value: string) => void; + }; +}; + +const PaymentAgentWithdrawalLockedItem = ({ item }: TPaymentAgentWithdrawalLockedItemProps) => { return (
{item.title && ( @@ -31,7 +48,7 @@ const PaymentAgentWithdrawalLockedItem = ({ item }) => { ); }; -const PaymentAgentWithdrawalLocked = ({ error, history }) => { +const PaymentAgentWithdrawalLocked = ({ error, history }: TPaymentAgentWithdrawalLockedProps) => { const items = [ ...(error.code === 'PaymentAgentWithdrawSameMethod' ? [ @@ -63,8 +80,7 @@ const PaymentAgentWithdrawalLocked = ({ error, history }) => { if ( error.onClickButton || - error.code !== 'PaymentAgentWithdrawSameMethod' || - error.code !== 'PaymentAgentUseOtherMethod' + (error.code !== 'PaymentAgentWithdrawSameMethod' && error.code !== 'PaymentAgentUseOtherMethod') ) { return ; } @@ -78,13 +94,4 @@ const PaymentAgentWithdrawalLocked = ({ error, history }) => { ); }; -PaymentAgentWithdrawalLocked.propTypes = { - error: PropTypes.object, - history: PropTypes.object, -}; - -PaymentAgentWithdrawalLockedItem.propTypes = { - item: PropTypes.object, -}; - export default withRouter(PaymentAgentWithdrawalLocked); diff --git a/packages/cashier/src/pages/payment-agent/payment-agent.jsx b/packages/cashier/src/pages/payment-agent/payment-agent.tsx similarity index 90% rename from packages/cashier/src/pages/payment-agent/payment-agent.jsx rename to packages/cashier/src/pages/payment-agent/payment-agent.tsx index 7fcdcbfba619..597b30c9bcb8 100644 --- a/packages/cashier/src/pages/payment-agent/payment-agent.jsx +++ b/packages/cashier/src/pages/payment-agent/payment-agent.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import { Loading } from '@deriv/components'; import { observer, useStore } from '@deriv/stores'; @@ -8,7 +7,11 @@ import PaymentAgentList from './payment-agent-list'; import { useCashierStore } from '../../stores/useCashierStores'; import { useCashierLocked } from '@deriv/hooks'; -const PaymentAgent = observer(({ setSideNotes }) => { +type TPaymentAgent = { + setSideNotes: (notes: (React.ReactNode | React.ReactNode[])[] | null) => void; +}; + +const PaymentAgent = observer(({ setSideNotes }: TPaymentAgent) => { const { client } = useStore(); const { is_switching, @@ -52,8 +55,4 @@ const PaymentAgent = observer(({ setSideNotes }) => { return ; }); -PaymentAgent.propTypes = { - setSideNotes: PropTypes.func, -}; - export default PaymentAgent; diff --git a/packages/cashier/src/pages/withdrawal/__tests__/withdrawal.spec.tsx b/packages/cashier/src/pages/withdrawal/__tests__/withdrawal.spec.tsx index 86a594f55edd..5e7b37a6015b 100644 --- a/packages/cashier/src/pages/withdrawal/__tests__/withdrawal.spec.tsx +++ b/packages/cashier/src/pages/withdrawal/__tests__/withdrawal.spec.tsx @@ -5,7 +5,7 @@ import { createBrowserHistory } from 'history'; import { isDesktop } from '@deriv/shared'; import Withdrawal from '../withdrawal'; import CashierProviders from '../../../cashier-providers'; -import { mockStore, TStores } from '@deriv/stores'; +import { mockStore } from '@deriv/stores'; import { useCashierLocked } from '@deriv/hooks'; jest.mock('Components/cashier-locked', () => jest.fn(() => 'CashierLocked')); @@ -68,15 +68,14 @@ describe('', () => { mockUseCashierLocked.mockReturnValue(false); }); - const renderWithdrawal = (mock_root_store: TStores, is_rerender = false) => { - const ui = ( + const mockWithdrawal = (mock_root_store: ReturnType, is_rerender = false) => { + return ( ); - return is_rerender ? ui : render(ui); }; it('should render component', () => { @@ -97,7 +96,7 @@ describe('', () => { }, }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('CashierLocked')).toBeInTheDocument(); }); @@ -118,7 +117,7 @@ describe('', () => { }, }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('Loading')).toBeInTheDocument(); }); @@ -132,7 +131,7 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('Virtual')).toBeInTheDocument(); }); @@ -146,7 +145,7 @@ describe('', () => { modules: { cashier: cashier_mock }, }); mockUseCashierLocked.mockReturnValue(true); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('CashierLocked')).toBeInTheDocument(); }); @@ -167,12 +166,12 @@ describe('', () => { }, }, }); - renderWithdrawal(mock_root_store); + const { rerender } = render(mockWithdrawal(mock_root_store)); expect(screen.getByText('WithdrawalLocked')).toBeInTheDocument(); mock_root_store.modules.cashier.withdraw.is_10k_withdrawal_limit_reached = true; - renderWithdrawal(mock_root_store, true); + rerender(mockWithdrawal(mock_root_store)); expect(screen.getByText('WithdrawalLocked')).toBeInTheDocument(); }); @@ -185,7 +184,7 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('NoBalance')).toBeInTheDocument(); }); @@ -210,12 +209,12 @@ describe('', () => { }, }, }); - const { rerender } = renderWithdrawal(mock_root_store) as ReturnType; + const { rerender } = render(mockWithdrawal(mock_root_store)); expect(screen.getByText('Error')).toBeInTheDocument(); mock_root_store.modules.cashier.withdraw.verification.error = { message: 'Error message' }; - rerender(renderWithdrawal(mock_root_store, true) as JSX.Element); + rerender(mockWithdrawal(mock_root_store)); expect(screen.getByText('Error')).toBeInTheDocument(); }); @@ -229,11 +228,12 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - const { rerender } = renderWithdrawal(mock_root_store) as ReturnType; + + const { rerender } = render(mockWithdrawal(mock_root_store)); expect(screen.getByText('Withdraw')).toBeInTheDocument(); mock_root_store.modules.cashier.iframe.iframe_url = 'coiframe_urlde'; - rerender(renderWithdrawal(mock_root_store, true) as JSX.Element); + rerender(mockWithdrawal(mock_root_store)); expect(screen.getByText('Withdraw')).toBeInTheDocument(); }); @@ -255,7 +255,7 @@ describe('', () => { }, }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('CryptoWithdrawForm')).toBeInTheDocument(); }); @@ -276,7 +276,7 @@ describe('', () => { }, }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('CryptoWithdrawReceipt')).toBeInTheDocument(); }); @@ -297,7 +297,7 @@ describe('', () => { }, }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('CryptoTransactionsHistory')).toBeInTheDocument(); }); @@ -310,7 +310,7 @@ describe('', () => { }, modules: { cashier: cashier_mock }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(screen.getByText('WithdrawalVerificationEmail')).toBeInTheDocument(); }); @@ -335,7 +335,7 @@ describe('', () => { }); (isDesktop as jest.Mock).mockReturnValueOnce(false); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(setSideNotes).not.toHaveBeenCalled(); }); @@ -356,7 +356,7 @@ describe('', () => { }, }, }); - renderWithdrawal(mock_root_store); + render(mockWithdrawal(mock_root_store)); expect(setSideNotes).toHaveBeenCalledTimes(1); }); diff --git a/packages/cashier/src/pages/withdrawal/crypto-withdraw-form/__tests__/crypto-withdraw-form.spec.tsx b/packages/cashier/src/pages/withdrawal/crypto-withdraw-form/__tests__/crypto-withdraw-form.spec.tsx index 19661b9fec09..7789c9d325c8 100644 --- a/packages/cashier/src/pages/withdrawal/crypto-withdraw-form/__tests__/crypto-withdraw-form.spec.tsx +++ b/packages/cashier/src/pages/withdrawal/crypto-withdraw-form/__tests__/crypto-withdraw-form.spec.tsx @@ -3,11 +3,12 @@ import { act } from 'react-dom/test-utils'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import CryptoWithdrawForm from '../crypto-withdraw-form'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { currency: 'BTC', verification_code: { payment_withdraw: 'code' }, @@ -36,7 +37,7 @@ describe('', () => { }, }, }, - }; + }); }); const renderCryptoWithdrawForm = () => { diff --git a/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/__tests__/crypto-withdraw-receipt.spec.tsx b/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/__tests__/crypto-withdraw-receipt.spec.tsx index e282491be417..4bfd59ce70f7 100644 --- a/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/__tests__/crypto-withdraw-receipt.spec.tsx +++ b/packages/cashier/src/pages/withdrawal/crypto-withdraw-receipt/__tests__/crypto-withdraw-receipt.spec.tsx @@ -2,11 +2,12 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import CryptoWithdrawReceipt from '../crypto-withdraw-receipt'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { currency: 'BTC', }, @@ -38,7 +39,7 @@ describe('', () => { }, }, }, - }; + }); }); const renderCryptoWithdrawReceipt = () => { diff --git a/packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.tsx b/packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.tsx index 039d1b69e7a7..b0784d188a8a 100644 --- a/packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.tsx +++ b/packages/cashier/src/pages/withdrawal/withdraw/__tests__/withdraw.spec.tsx @@ -2,13 +2,14 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import Withdraw from '../withdraw'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; jest.mock('Components/cashier-container/real', () => jest.fn(() => 'mockedReal')); describe('', () => { - let mockRootStore; + let mockRootStore: ReturnType; beforeEach(() => { - mockRootStore = { + mockRootStore = mockStore({ client: { verification_code: { payment_withdraw: 'code' }, }, @@ -30,7 +31,7 @@ describe('', () => { }, }, }, - }; + }); }); it('should render the cashier container component', () => { diff --git a/packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.tsx b/packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.tsx index ff0d331cd87c..30e0171b39db 100644 --- a/packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.tsx +++ b/packages/cashier/src/pages/withdrawal/withdrawal-locked/__tests__/withdrawal-locked.spec.tsx @@ -1,12 +1,14 @@ import React from 'react'; import { Router } from 'react-router'; -import { createBrowserHistory } from 'history'; +import { BrowserHistory, createBrowserHistory } from 'history'; import { fireEvent, render, screen } from '@testing-library/react'; import { routes } from '@deriv/shared'; import WithdrawalLocked from '../withdrawal-locked'; import CashierProviders from '../../../../cashier-providers'; +import { mockStore } from '@deriv/stores'; +import { GetAccountStatus } from '@deriv/api-types'; -type TStatus = 'document' | 'none' | 'pending' | ''; +type TStatus = Pick['authentication'], 'identity' | 'document' | 'needs_verification'>; jest.mock('Components/cashier-locked', () => jest.fn(() => 'CashierLocked')); @@ -33,7 +35,11 @@ const fireButtonEvent = (button: 'proof_of_identity_btn' | 'proof_of_address_btn } }; -const setAccountStatus = (identity_status: TStatus, document_status: TStatus, needs_verification: TStatus) => { +const setAccountStatus = ( + identity_status: Required['identity']['status'], + document_status: Required['document']['status'], + needs_verification: Required['needs_verification'][0] +) => { return { authentication: { identity: { @@ -48,12 +54,12 @@ const setAccountStatus = (identity_status: TStatus, document_status: TStatus, ne }; describe('WithdrawalLocked', () => { - let history, mockRootStore; + let history: BrowserHistory, mockRootStore: ReturnType; beforeEach(() => { history = createBrowserHistory(); - mockRootStore = { + mockRootStore = mockStore({ client: { - account_status: setAccountStatus('pending', '', ''), + account_status: setAccountStatus('pending', 'none', ''), }, modules: { cashier: { @@ -65,7 +71,7 @@ describe('WithdrawalLocked', () => { }, }, }, - }; + }); }); const renderWithdrawalLocked = () => { @@ -86,7 +92,7 @@ describe('WithdrawalLocked', () => { }); it('Should show "Upload a proof of identity to verify your identity" message and redirect to account/proof-of-identity when "-->" button clicked', () => { - mockRootStore.client.account_status = setAccountStatus('none', '', ''); + mockRootStore.client.account_status = setAccountStatus('none', 'none', '') as GetAccountStatus; renderWithdrawalLocked(); fireButtonEvent('proof_of_identity_btn'); @@ -94,7 +100,7 @@ describe('WithdrawalLocked', () => { }); it('Should show "Check proof of address document verification status" message and redirect to account/proof_of_address when "-->" button clicked', () => { - mockRootStore.client.account_status = setAccountStatus('', 'pending', 'document'); + mockRootStore.client.account_status = setAccountStatus('none', 'pending', 'document') as GetAccountStatus; renderWithdrawalLocked(); fireButtonEvent('proof_of_address_btn'); @@ -102,7 +108,7 @@ describe('WithdrawalLocked', () => { }); it('Should show "Upload a proof of address to verify your address" message and redirect to account/proof_of_address when "-->" button clicked', () => { - mockRootStore.client.account_status = setAccountStatus('', 'none', 'document'); + mockRootStore.client.account_status = setAccountStatus('none', 'none', 'document') as GetAccountStatus; renderWithdrawalLocked(); fireButtonEvent('proof_of_address_btn'); @@ -110,7 +116,7 @@ describe('WithdrawalLocked', () => { }); it('Should show "Complete the financial assessment form" message and redirect to account/financial_assessment when "-->" button clicked', () => { - mockRootStore.client.account_status = setAccountStatus('', '', ''); + mockRootStore.client.account_status = setAccountStatus('none', 'none', '') as GetAccountStatus; mockRootStore.modules.cashier.withdraw.error.is_ask_financial_risk_approval = true; renderWithdrawalLocked(); @@ -119,7 +125,7 @@ describe('WithdrawalLocked', () => { }); it('should render component', () => { - mockRootStore.client.account_status = setAccountStatus('', '', ''); + mockRootStore.client.account_status = setAccountStatus('none', 'none', '') as GetAccountStatus; mockRootStore.modules.cashier.withdraw.is_10k_withdrawal_limit_reached = false; renderWithdrawalLocked(); diff --git a/packages/cashier/src/stores/__tests__/account-prompt-dialog-store.spec.ts b/packages/cashier/src/stores/__tests__/account-prompt-dialog-store.spec.ts index edcb5e59ef0f..2c51116cc907 100644 --- a/packages/cashier/src/stores/__tests__/account-prompt-dialog-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/account-prompt-dialog-store.spec.ts @@ -1,10 +1,11 @@ import { routes } from '@deriv/shared'; -import { TRootStore } from 'Types'; +import { mockStore } from '@deriv/stores'; import AccountPromptDialogStore from '../account-prompt-dialog-store'; +import { TRootStore } from '../../types'; describe('AccountPromptDialogStore', () => { let account_prompt_dialog_store: AccountPromptDialogStore; - const root_store: DeepPartial = { + const root_store = mockStore({ common: { routeTo: jest.fn(), }, @@ -29,10 +30,9 @@ describe('AccountPromptDialogStore', () => { }, }, }, - }; + }); beforeEach(() => { - // TODO: Check this account_prompt_dialog_store = new AccountPromptDialogStore(root_store as TRootStore); }); diff --git a/packages/cashier/src/stores/__tests__/account-transfer-store.spec.ts b/packages/cashier/src/stores/__tests__/account-transfer-store.spec.ts index 451be7b0c72b..cce2aa5ddb07 100644 --- a/packages/cashier/src/stores/__tests__/account-transfer-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/account-transfer-store.spec.ts @@ -1,13 +1,14 @@ import AccountTransferStore from '../account-transfer-store'; import { getCurrencies, validNumber, CFD_PLATFORMS } from '@deriv/shared'; import { configure } from 'mobx'; -import type { TTransferAccount, TRootStore, TWebSocket } from 'Types'; +import type { TTransferAccount, TWebSocket, TRootStore } from '../../types'; +import { mockStore } from '@deriv/stores'; configure({ safeDescriptors: false }); let accounts: TTransferAccount[], account_transfer_store: AccountTransferStore, - root_store: DeepPartial, + root_store: TRootStore, WS: DeepPartial; const CR_eUSDT_account: TTransferAccount = { @@ -135,7 +136,7 @@ beforeEach(() => { }), wait: jest.fn(), }; - root_store = { + root_store = mockStore({ client: { account_status: { status: ['status'], @@ -183,8 +184,8 @@ beforeEach(() => { }, combined_cfd_mt5_accounts: [], }, - }; - account_transfer_store = new AccountTransferStore(WS as TWebSocket, root_store as TRootStore); + }) as TRootStore; + account_transfer_store = new AccountTransferStore(WS as TWebSocket, root_store); }); jest.mock('@deriv/shared', () => ({ @@ -347,7 +348,7 @@ describe('AccountTransferStore', () => { }); it('should set transfer fee equal to 2', () => { - getCurrencies.mockReturnValueOnce({ USD: { transfer_between_accounts: { fees: { BTC: 2 } } } }); + (getCurrencies as jest.Mock).mockReturnValueOnce({ USD: { transfer_between_accounts: { fees: { BTC: 2 } } } }); account_transfer_store.setSelectedFrom({ currency: 'USD' }); account_transfer_store.setSelectedTo({ currency: 'BTC' }); account_transfer_store.setTransferFee(); @@ -356,7 +357,9 @@ describe('AccountTransferStore', () => { }); it('should set transfer fee equal to 0 if transfer fee is undefined', () => { - getCurrencies.mockReturnValueOnce({ USD: { transfer_between_accounts: { fees: { BTC: undefined } } } }); + (getCurrencies as jest.Mock).mockReturnValueOnce({ + USD: { transfer_between_accounts: { fees: { BTC: undefined } } }, + }); account_transfer_store.setSelectedFrom({ currency: 'USD' }); account_transfer_store.setSelectedTo({ currency: 'BTC' }); account_transfer_store.setTransferFee(); @@ -379,7 +382,7 @@ describe('AccountTransferStore', () => { }); it('should set proper transfer limit for mt5 transfer', () => { - getCurrencies.mockReturnValueOnce({ + (getCurrencies as jest.Mock).mockReturnValueOnce({ USD: { transfer_between_accounts: { limits_mt5: { min: 1, max: 10 } } }, }); @@ -390,7 +393,7 @@ describe('AccountTransferStore', () => { }); it('should set proper transfer limit for dxtrade transfer', () => { - getCurrencies.mockReturnValueOnce({ + (getCurrencies as jest.Mock).mockReturnValueOnce({ USD: { transfer_between_accounts: { limits_dxtrade: { min: 10, max: 100 } } }, }); @@ -401,7 +404,7 @@ describe('AccountTransferStore', () => { }); it('should set proper transfer limit for other types of transfers', () => { - getCurrencies.mockReturnValueOnce({ + (getCurrencies as jest.Mock).mockReturnValueOnce({ USD: { transfer_between_accounts: { balance: 500, limits: { min: 100, max: 1000 } } }, }); @@ -412,7 +415,7 @@ describe('AccountTransferStore', () => { }); it('should set max transfer limit equal to the current "selected from" balance, if there is no max transfer fee in response', () => { - getCurrencies.mockReturnValueOnce({ + (getCurrencies as jest.Mock).mockReturnValueOnce({ USD: { transfer_between_accounts: { balance: 500, limits: { min: 100 } } }, }); @@ -423,7 +426,7 @@ describe('AccountTransferStore', () => { }); it('should set min transfer limit equal to null, if there is no min transfer fee in response', () => { - getCurrencies.mockReturnValueOnce({ + (getCurrencies as jest.Mock).mockReturnValueOnce({ USD: { transfer_between_accounts: { balance: 500, limits: { max: 1000 } } }, }); @@ -543,9 +546,9 @@ describe('AccountTransferStore', () => { }); it('should set transferred amount in receipt', () => { - account_transfer_store.setReceiptTransfer({ amount: 1000 }); + account_transfer_store.setReceiptTransfer({ amount: '1000' }); - expect(account_transfer_store.receipt.amount_transferred).toBe(1000); + expect(account_transfer_store.receipt.amount_transferred).toBe('1000'); }); it('should switch the value of selected_from and selected_to, if new value of selected_from is the same as the current selected_to', async () => { @@ -704,10 +707,9 @@ describe('AccountTransferStore', () => { await account_transfer_store.sortAccountsTransfer({ accounts }); await account_transfer_store.requestTransferBetweenAccounts({ amount: 10 }); - expect(account_transfer_store.root_store.modules.cashier.general_store.setLoading.mock.calls).toEqual([ - [true], - [false], - ]); + expect( + (account_transfer_store.root_store.modules.cashier.general_store.setLoading as jest.Mock).mock.calls + ).toEqual([[true], [false]]); }); it('should call WS.mt5LoginList and WS.balanceAll methods to update the balance for mt account when calling requestTransferBetweenAccounts', async () => { @@ -747,31 +749,31 @@ describe('AccountTransferStore', () => { const spyValidateTransferFromAmount = jest.spyOn(account_transfer_store, 'validateTransferFromAmount'); account_transfer_store.setSelectedFrom({ currency: 'USD' }); account_transfer_store.setSelectedTo({ currency: 'BTC' }); - account_transfer_store.setTransferPercentageSelectorResult(10); + account_transfer_store.setTransferPercentageSelectorResult('10'); const { onChangeConverterFromAmount, setConverterFromAmount } = account_transfer_store.root_store.modules.cashier.crypto_fiat_converter; - expect(setConverterFromAmount).toHaveBeenCalledWith(10); + expect(setConverterFromAmount).toHaveBeenCalledWith('10'); expect(spyValidateTransferFromAmount).toHaveBeenCalledTimes(1); - expect(onChangeConverterFromAmount).toHaveBeenCalledWith({ target: { value: 10 } }, 'USD', 'BTC'); + expect(onChangeConverterFromAmount).toHaveBeenCalledWith({ target: { value: '10' } }, 'USD', 'BTC'); }); it('should set transfer percentage selector result if selected_from.balance = 0', () => { const spyValidateTransferFromAmount = jest.spyOn(account_transfer_store, 'validateTransferFromAmount'); account_transfer_store.setSelectedFrom({ balance: 0, currency: 'USD' }); account_transfer_store.setSelectedTo({ currency: 'BTC' }); - account_transfer_store.setTransferPercentageSelectorResult(0); + account_transfer_store.setTransferPercentageSelectorResult('0'); const { onChangeConverterFromAmount, setConverterFromAmount } = account_transfer_store.root_store.modules.cashier.crypto_fiat_converter; - expect(setConverterFromAmount).toHaveBeenCalledWith(0); + expect(setConverterFromAmount).toHaveBeenCalledWith('0'); expect(spyValidateTransferFromAmount).toHaveBeenCalledTimes(1); - expect(onChangeConverterFromAmount).toHaveBeenCalledWith({ target: { value: 0 } }, 'USD', 'BTC'); + expect(onChangeConverterFromAmount).toHaveBeenCalledWith({ target: { value: '0' } }, 'USD', 'BTC'); }); it('should reset crypto fiat converter if amount = 0 and selected_from.balance > 0', () => { account_transfer_store.setSelectedFrom({ balance: 10, currency: 'USD' }); - account_transfer_store.setTransferPercentageSelectorResult(0); + account_transfer_store.setTransferPercentageSelectorResult('0'); expect( account_transfer_store.root_store.modules.cashier.crypto_fiat_converter.resetConverter @@ -779,7 +781,7 @@ describe('AccountTransferStore', () => { }); it('should set timer visibility and percentage selector selection status to false when calling setTransferPercentageSelectorResult method', () => { - account_transfer_store.setTransferPercentageSelectorResult(10); + account_transfer_store.setTransferPercentageSelectorResult('10'); const { crypto_fiat_converter, general_store } = account_transfer_store.root_store.modules.cashier; expect(crypto_fiat_converter.setIsTimerVisible).toHaveBeenCalledWith(false); @@ -805,7 +807,7 @@ describe('AccountTransferStore', () => { }); it('should set error message, if converter_from_amount is not valid number when calling validateTransferFromAmount method', () => { - validNumber.mockReturnValueOnce({ is_ok: false, message: 'Validation error' }); + (validNumber as jest.Mock).mockReturnValueOnce({ is_ok: false, message: 'Validation error' }); account_transfer_store.validateTransferFromAmount(); expect( @@ -823,7 +825,7 @@ describe('AccountTransferStore', () => { }); it('should set error message, if converter_to_amount is not valid number when calling validateTransferToAmount method', () => { - validNumber.mockReturnValueOnce({ is_ok: false, message: 'Validation error' }); + (validNumber as jest.Mock).mockReturnValueOnce({ is_ok: false, message: 'Validation error' }); account_transfer_store.validateTransferToAmount(); expect( diff --git a/packages/cashier/src/stores/__tests__/crypto-fiat-converter-store.spec.ts b/packages/cashier/src/stores/__tests__/crypto-fiat-converter-store.spec.ts index ae3dc1eaa26a..01e0e61ce4e0 100644 --- a/packages/cashier/src/stores/__tests__/crypto-fiat-converter-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/crypto-fiat-converter-store.spec.ts @@ -1,8 +1,9 @@ -import { TRootStore, TWebSocket } from 'Types'; +import { TWebSocket, TRootStore } from '../../types'; +import { mockStore } from '@deriv/stores'; import CryptoFiatConverterStore from '../crypto-fiat-converter-store'; let crypto_fiat_converter_store: CryptoFiatConverterStore, - root_store: DeepPartial, + root_store: ReturnType, WS: DeepPartial; beforeEach(() => { @@ -17,7 +18,7 @@ beforeEach(() => { }, }), }; - root_store = { + root_store = mockStore({ modules: { cashier: { account_transfer: { @@ -37,7 +38,7 @@ beforeEach(() => { }, }, }, - }; + }); crypto_fiat_converter_store = new CryptoFiatConverterStore(WS as TWebSocket, root_store as TRootStore); }); diff --git a/packages/cashier/src/stores/__tests__/deposit-store.spec.ts b/packages/cashier/src/stores/__tests__/deposit-store.spec.ts index 7752160e57fa..61875a46938e 100644 --- a/packages/cashier/src/stores/__tests__/deposit-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/deposit-store.spec.ts @@ -1,6 +1,7 @@ import DepositStore from '../deposit-store'; import { configure } from 'mobx'; -import { TRootStore, TWebSocket } from 'Types'; +import { TRootStore, TWebSocket } from '../../types'; +import { mockStore } from '@deriv/stores'; configure({ safeDescriptors: false }); @@ -8,7 +9,7 @@ describe('DepositStore', () => { let deposit_store: DepositStore; beforeEach(() => { - const root_store: DeepPartial = { + const root_store = mockStore({ client: { is_virtual: false, updateAccountStatus: jest.fn(), @@ -33,7 +34,7 @@ describe('DepositStore', () => { }, }, }, - }; + }); const WS: DeepPartial = { authorized: { cashier: jest.fn(() => Promise.resolve({ cashier: 'https://cashier.deriv.com' })), diff --git a/packages/cashier/src/stores/__tests__/general-store.spec.ts b/packages/cashier/src/stores/__tests__/general-store.spec.ts index bd22a6022407..d08c1691e2bb 100644 --- a/packages/cashier/src/stores/__tests__/general-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/general-store.spec.ts @@ -2,14 +2,15 @@ import { configure } from 'mobx'; import { waitFor } from '@testing-library/react'; import { routes, ContentFlag } from '@deriv/shared'; import GeneralStore from '../general-store'; -import type { TWebSocket, TRootStore } from 'Types'; +import type { TWebSocket, TRootStore } from '../../types'; +import { mockStore } from '@deriv/stores'; configure({ safeDescriptors: false }); -let general_store: GeneralStore, root_store: DeepPartial, WS: DeepPartial; +let general_store: GeneralStore, root_store: ReturnType, WS: DeepPartial; beforeEach(() => { - root_store = { + root_store = mockStore({ client: { account_list: [{ is_virtual: false, title: 'USD' }], account_status: { @@ -71,7 +72,7 @@ beforeEach(() => { toggleSetCurrencyModal: jest.fn(), }, traders_hub: { content_flag: ContentFlag.CR_DEMO }, - }; + }); WS = { authorized: { p2pAdvertiserInfo: jest.fn().mockResolvedValueOnce({ error: { code: 'advertiser_error' } }), @@ -83,11 +84,10 @@ beforeEach(() => { describe('GeneralStore', () => { it('should set function on remount', () => { - // TODO: Check this - // const remountFunc = () => 'function'; + // TODO: use the actual function smh general_store.setOnRemount('function'); - expect(general_store.onRemount).toBe('function'); + expect(general_store.onRemount).toEqual('function'); }); it('should return false if the client currency is equal to USD when is_crypto property was called', () => { @@ -126,7 +126,7 @@ describe('GeneralStore', () => { }); it('should calculate proper percentage for account transfer container', () => { - general_store.root_store.modules.cashier.crypto_fiat_converter.converter_from_amount = 500; + general_store.root_store.modules.cashier.crypto_fiat_converter.converter_from_amount = '500'; general_store.root_store.modules.cashier.account_transfer.selected_from.balance = 10000; general_store.setActiveTab('account_transfer'); general_store.calculatePercentage(); @@ -137,7 +137,7 @@ describe('GeneralStore', () => { it('should calculate proper percentage for other containers', () => { general_store.root_store.client.balance = '9000'; general_store.setActiveTab('deposit'); - general_store.calculatePercentage(1000); + general_store.calculatePercentage('1000'); expect(general_store.percentage).toBe(11); }); @@ -223,7 +223,9 @@ describe('GeneralStore', () => { pathname: routes.cashier_pa, }, })); - general_store.root_store.modules.cashier.payment_agent.filterPaymentAgentList.mockResolvedValueOnce([]); + ( + general_store.root_store.modules.cashier.payment_agent.filterPaymentAgentList as jest.Mock + ).mockResolvedValueOnce([]); general_store.root_store.client.is_logged_in = true; await general_store.onMountCommon(false); diff --git a/packages/cashier/src/stores/__tests__/iframe-store.spec.ts b/packages/cashier/src/stores/__tests__/iframe-store.spec.ts index e25628570eab..d64b5548f805 100644 --- a/packages/cashier/src/stores/__tests__/iframe-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/iframe-store.spec.ts @@ -1,13 +1,14 @@ import IframeStore from '../iframe-store'; import { configure } from 'mobx'; -import { TRootStore } from 'Types'; +import { TRootStore } from '../../types'; +import { mockStore } from '@deriv/stores'; configure({ safeDescriptors: false }); -let iframe_store: IframeStore, root_store: DeepPartial; +let iframe_store: IframeStore, root_store: TRootStore; beforeEach(() => { - root_store = { + root_store = mockStore({ client: { setVerificationCode: jest.fn(), }, @@ -23,8 +24,8 @@ beforeEach(() => { is_dark_mode_on: true, is_mobile: false, }, - }; - iframe_store = new IframeStore(root_store as TRootStore); + }) as TRootStore; + iframe_store = new IframeStore(root_store); }); describe('IframeStore', () => { diff --git a/packages/cashier/src/stores/__tests__/on-ramp-store.spec.ts b/packages/cashier/src/stores/__tests__/on-ramp-store.spec.ts index cfcbd54edbca..3c7dd65c37f4 100644 --- a/packages/cashier/src/stores/__tests__/on-ramp-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/on-ramp-store.spec.ts @@ -2,23 +2,24 @@ import { waitFor } from '@testing-library/react'; import OnRampStore from '../on-ramp-store'; import createBanxaProvider from '../../pages/on-ramp/on-ramp-providers'; import { configure } from 'mobx'; -import { TRootStore, TWebSocket, TOnRampProvider } from 'Types'; +import { TWebSocket, TOnRampProvider, TRootStore } from '../../types'; +import { mockStore } from '@deriv/stores'; configure({ safeDescriptors: false }); let banxa_provider: TOnRampProvider, onramp_store: OnRampStore, onramp_providers: TOnRampProvider[], - root_store: DeepPartial, + root_store: ReturnType, WS: DeepPartial; beforeEach(() => { - root_store = { + root_store = mockStore({ client: { is_virtual: false, currency: 'BTC', }, - }; + }); WS = { authorized: { cashier: jest.fn().mockResolvedValueOnce({ @@ -77,16 +78,16 @@ describe('OnRampStore', () => { expect(onramp_store.onramp_popup_modal_title).toBe('Payment channel'); }); - it('should return proper onramp popup modal title if should_show_widget = false and there is selected provider with should_show_dialog = true', () => { - onramp_store.setSelectedProvider(banxa_provider); - onramp_store.setApiError('API Error'); + it('should return proper onramp popup modal title if should_show_widget = false and there is selected provider with should_show_dialog = true', async () => { + await onramp_store.setSelectedProvider(banxa_provider); + onramp_store.setApiError({ code: 'API Error', message: 'API Error' }); expect(onramp_store.onramp_popup_modal_title).toBe('Our server cannot retrieve an address.'); }); - it('should return empty string to render header + close icon if should_show_widget = false and there is selected provider with should_show_dialog = false', () => { - onramp_store.setSelectedProvider(banxa_provider); - onramp_store.setApiError(''); + it('should return empty string to render header + close icon if should_show_widget = false and there is selected provider with should_show_dialog = false', async () => { + await onramp_store.setSelectedProvider(banxa_provider); + onramp_store.setApiError(null); expect(onramp_store.onramp_popup_modal_title).toBe(' '); }); @@ -95,12 +96,12 @@ describe('OnRampStore', () => { expect(onramp_store.onramp_popup_modal_title).toBe(undefined); }); - it('should have returned from onMountOnramp method if there is no selected_provider', () => { + it('should have returned from onMountOnramp method if there is no selected_provider', async () => { const spyOnMountOnramp = jest.spyOn(onramp_store, 'onMountOnramp'); onramp_store.onMountOnramp(); banxa_provider.getScriptDependencies = jest.fn().mockReturnValueOnce(['dependency']); - onramp_store.setSelectedProvider(banxa_provider); - onramp_store.setSelectedProvider(); + await onramp_store.setSelectedProvider(banxa_provider); + await onramp_store.setSelectedProvider(); expect(spyOnMountOnramp).toHaveReturned(); }); @@ -109,14 +110,14 @@ describe('OnRampStore', () => { const spyOnMountOnramp = jest.spyOn(onramp_store, 'onMountOnramp'); onramp_store.onMountOnramp(); banxa_provider.getScriptDependencies = jest.fn().mockReturnValueOnce([]); - onramp_store.setSelectedProvider(banxa_provider); + await onramp_store.setSelectedProvider(banxa_provider); expect(spyOnMountOnramp).toHaveReturned(); }); it('should set widget html if it is defined when disposeGetWidgetHtmlReaction reaction is running', async () => { const spySetWidgetHtml = jest.spyOn(onramp_store, 'setWidgetHtml'); - onramp_store.setSelectedProvider(banxa_provider); + await onramp_store.setSelectedProvider(banxa_provider); banxa_provider.getWidgetHtml = jest.fn().mockResolvedValueOnce('widget'); onramp_store.onMountOnramp(); onramp_store.setShouldShowWidget(true); @@ -126,7 +127,7 @@ describe('OnRampStore', () => { it('should set should_show_widget into false if html widget is not defined when disposeGetWidgetHtmlReaction reaction is running', async () => { const spySetShouldShowWidget = jest.spyOn(onramp_store, 'setShouldShowWidget'); - onramp_store.setSelectedProvider(banxa_provider); + await onramp_store.setSelectedProvider(banxa_provider); banxa_provider.getWidgetHtml = jest.fn().mockResolvedValueOnce(''); onramp_store.onMountOnramp(); onramp_store.setShouldShowWidget(true); @@ -138,7 +139,7 @@ describe('OnRampStore', () => { it('should set widget error if there is an error when requesting widget when disposeGetWidgetHtmlReaction reaction is running', async () => { const spySetWidgetError = jest.spyOn(onramp_store, 'setWidgetError'); - onramp_store.setSelectedProvider(banxa_provider); + await onramp_store.setSelectedProvider(banxa_provider); banxa_provider.getWidgetHtml = jest.fn().mockRejectedValueOnce('Request error'); onramp_store.onMountOnramp(); onramp_store.setShouldShowWidget(true); @@ -148,10 +149,10 @@ describe('OnRampStore', () => { }); }); - it('should not call setIsRequestingWidgetHtml method if is_requesting_widget_html already equal to true when disposeGetWidgetHtmlReaction reaction is running', () => { + it('should not call setIsRequestingWidgetHtml method if is_requesting_widget_html already equal to true when disposeGetWidgetHtmlReaction reaction is running', async () => { const spySetIsRequestingWidgetHtml = jest.spyOn(onramp_store, 'setIsRequestingWidgetHtml'); onramp_store.is_requesting_widget_html = true; - onramp_store.setSelectedProvider(banxa_provider); + await onramp_store.setSelectedProvider(banxa_provider); onramp_store.onMountOnramp(); onramp_store.setShouldShowWidget(true); @@ -174,9 +175,9 @@ describe('OnRampStore', () => { expect(onramp_store.should_show_widget).toBeTruthy(); }); - it('should go to deposit page when onClickGoToDepositPage method was called', () => { + it('should go to deposit page when onClickGoToDepositPage method was called', async () => { window.open = jest.fn(); - onramp_store.onClickGoToDepositPage(); + await onramp_store.onClickGoToDepositPage(); expect(window.open).toHaveBeenCalledWith('https://app.deriv.com/cashier/deposit'); @@ -186,10 +187,12 @@ describe('OnRampStore', () => { it('should set api error and clear deposit address interval if there is an error in response when pollApiForDepositAddress method was called', async () => { jest.useFakeTimers(); const spySetApiError = jest.spyOn(onramp_store, 'setApiError'); - onramp_store.WS.authorized.cashier = jest.fn().mockResolvedValueOnce({ error: 'API error' }); - onramp_store.pollApiForDepositAddress(false); + onramp_store.WS.authorized.cashier = jest + .fn() + .mockResolvedValueOnce({ error: { code: 'API Error', message: 'API Error' } }); + await onramp_store.pollApiForDepositAddress(false); - expect(await spySetApiError).toHaveBeenLastCalledWith('API error'); + expect(await spySetApiError).toHaveBeenLastCalledWith({ code: 'API Error', message: 'API Error' }); expect(clearInterval).toHaveBeenCalledTimes(1); jest.useRealTimers(); @@ -199,7 +202,7 @@ describe('OnRampStore', () => { jest.useFakeTimers(); const spySetDepositAddress = jest.spyOn(onramp_store, 'setDepositAddress'); onramp_store.WS.authorized.cashier = jest.fn().mockResolvedValueOnce({ cashier: { deposit: { address: '' } } }); - onramp_store.pollApiForDepositAddress(true); + await onramp_store.pollApiForDepositAddress(true); expect(await spySetDepositAddress).toHaveBeenCalledWith(''); expect(clearInterval).toHaveBeenCalledTimes(1); @@ -210,7 +213,7 @@ describe('OnRampStore', () => { it('should set deposit address when pollApiForDepositAddress method was called with should_allow_empty_address = false', async () => { jest.useFakeTimers(); const spySetDepositAddress = jest.spyOn(onramp_store, 'setDepositAddress'); - onramp_store.pollApiForDepositAddress(false); + await onramp_store.pollApiForDepositAddress(false); expect(await spySetDepositAddress).toHaveBeenCalledWith('deposit address'); expect(clearInterval).toHaveBeenCalledTimes(1); @@ -220,7 +223,7 @@ describe('OnRampStore', () => { it('should set deposit address interval to 3 seconds when pollApiForDepositAddress method was called', async () => { jest.useFakeTimers(); - onramp_store.pollApiForDepositAddress(false); + await onramp_store.pollApiForDepositAddress(false); jest.runOnlyPendingTimers(); expect(setInterval).toHaveBeenCalledTimes(1); @@ -231,7 +234,7 @@ describe('OnRampStore', () => { it('should clear interval after 30 seconds if there is an empty deposit address in response when pollApiForDepositAddress method was called with should_allow_empty_address = false', async () => { jest.useFakeTimers(); - onramp_store.pollApiForDepositAddress(false); + await onramp_store.pollApiForDepositAddress(false); jest.runOnlyPendingTimers(); expect(setTimeout).toHaveBeenCalledTimes(1); @@ -242,13 +245,13 @@ describe('OnRampStore', () => { it('should set deposit address loading when pollApiForDepositAddress method was called', async () => { const spySetIsDepositAddressLoading = jest.spyOn(onramp_store, 'setIsDepositAddressLoading'); - onramp_store.pollApiForDepositAddress(false); + await onramp_store.pollApiForDepositAddress(false); - expect(await spySetIsDepositAddressLoading.mock.calls).toEqual([[true], [false]]); + expect(spySetIsDepositAddressLoading.mock.calls).toEqual([[true], [false]]); }); - it('should reset popup', () => { - onramp_store.resetPopup(); + it('should reset popup', async () => { + await onramp_store.resetPopup(); expect(onramp_store.api_error).toBeNull(); expect(onramp_store.deposit_address).toBeNull(); @@ -260,9 +263,9 @@ describe('OnRampStore', () => { }); it('should set api error', () => { - onramp_store.setApiError('API error'); + onramp_store.setApiError({ code: 'API Error', message: 'API Error' }); - expect(onramp_store.api_error).toBe('API error'); + expect(onramp_store.api_error).toEqual({ code: 'API Error', message: 'API Error' }); }); it('should set deposit address', () => { @@ -289,17 +292,17 @@ describe('OnRampStore', () => { expect(onramp_store.is_requesting_widget_html).toBeTruthy(); }); - it('should set selected provider', () => { + it('should set selected provider', async () => { const spyPollApiForDepositAddress = jest.spyOn(onramp_store, 'pollApiForDepositAddress'); const provider = createBanxaProvider(onramp_store); - onramp_store.setSelectedProvider(provider); + await onramp_store.setSelectedProvider(provider); expect(onramp_store.selected_provider).toBe(provider); expect(onramp_store.is_onramp_modal_open).toBeTruthy(); expect(spyPollApiForDepositAddress).toHaveBeenCalledWith(true); }); - it('should set selected provider to null if there is no provider', () => { - onramp_store.setSelectedProvider(); + it('should set selected provider to null if there is no provider', async () => { + await onramp_store.setSelectedProvider(); expect(onramp_store.selected_provider).toBeNull(); expect(onramp_store.is_onramp_modal_open).toBeFalsy(); diff --git a/packages/cashier/src/stores/__tests__/payment-agent-store.spec.js b/packages/cashier/src/stores/__tests__/payment-agent-store.spec.tsx similarity index 65% rename from packages/cashier/src/stores/__tests__/payment-agent-store.spec.js rename to packages/cashier/src/stores/__tests__/payment-agent-store.spec.tsx index ce0e1646b0b7..a6be3798dc7e 100644 --- a/packages/cashier/src/stores/__tests__/payment-agent-store.spec.js +++ b/packages/cashier/src/stores/__tests__/payment-agent-store.spec.tsx @@ -1,16 +1,18 @@ import { routes } from '@deriv/shared'; import PaymentAgentStore from '../payment-agent-store'; import { configure } from 'mobx'; +import { TRootStore, TWebSocket } from '../../types'; +import { mockStore } from '@deriv/stores'; configure({ safeDescriptors: false }); describe('PaymentAgentStore', () => { - let payment_agent_store; + let payment_agent_store: PaymentAgentStore, spyWindow: jest.SpyInstance; const mocked_payment_agent_list = { list: [ { currencies: 'USD', - deposit_commission: 0, + deposit_commission: '0', email: 'pa@example.com', further_information: 'further information', max_withdrawal: '2000', @@ -19,12 +21,12 @@ describe('PaymentAgentStore', () => { paymentagent_loginid: 'CR90000000', phone_numbers: [{ phone_number: '+12345678' }], supported_payment_methods: [{ payment_method: 'Visa' }], - urls: [{ url: 'http://www.pa.com' }], - withdrawal_commission: 0, + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', }, { currencies: 'USD', - deposit_commission: 0, + deposit_commission: '0', email: 'pa@example.com', further_information: 'further information', max_withdrawal: '2000', @@ -33,50 +35,50 @@ describe('PaymentAgentStore', () => { paymentagent_loginid: 'CR90000002', phone_numbers: [{ phone_number: '+12345678' }], supported_payment_methods: [{ payment_method: 'Visa' }, { payment_method: 'Mastercard' }], - urls: [{ url: 'http://www.pa.com' }], - withdrawal_commission: 0, + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', }, ], }; const mocked_payment_agents = [ { currency: 'USD', - deposit_commission: 0, + deposit_commission: '0', email: 'pa@example.com', further_information: 'further information', max_withdrawal: '2000', min_withdrawal: '10', name: 'Payment Agent of CR90000000', paymentagent_loginid: 'CR90000000', - phones: [{ phone_number: '+12345678' }], + phone_numbers: [{ phone_number: '+12345678' }], supported_banks: [{ payment_method: 'Visa' }], - urls: [{ url: 'http://www.pa.com' }], - withdrawal_commission: 0, + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', }, { currency: 'USD', - deposit_commission: 0, + deposit_commission: '0', email: 'pa@example.com', further_information: 'further information', max_withdrawal: '2000', min_withdrawal: '10', name: 'Payment Agent of CR90000002', paymentagent_loginid: 'CR90000002', - phones: [{ phone_number: '+12345678' }], + phone_numbers: [{ phone_number: '+12345678' }], supported_banks: [{ payment_method: 'Visa' }, { payment_method: 'Mastercard' }], - urls: [{ url: 'http://www.pa.com' }], - withdrawal_commission: 0, + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', }, ]; const mocked_withdrawal_request = { loginid: 'CR90000000', currency: 'USD', - amount: '200', + amount: 200, verification_code: 'abCDefXa', }; beforeEach(() => { - const root_store = { + const root_store = mockStore({ client: { currency: 'USD', residence: 'id', @@ -92,18 +94,18 @@ describe('PaymentAgentStore', () => { }, }, }, - }; + }); const WS = { allPaymentAgentList: () => Promise.resolve({ - paymentagent_list: mocked_payment_agent_list, + paymentagent_list: JSON.parse(JSON.stringify(mocked_payment_agent_list)), }), authorized: { paymentAgentDetails: () => Promise.resolve({ paymentagent_details: { max_withdrawal: '2000', min_withdrawal: '10' } }), paymentAgentList: jest.fn(() => Promise.resolve({ - paymentagent_list: mocked_payment_agent_list, + paymentagent_list: JSON.parse(JSON.stringify(mocked_payment_agent_list)), }) ), paymentAgentWithdraw: jest.fn(() => @@ -113,20 +115,23 @@ describe('PaymentAgentStore', () => { wait: () => Promise.resolve(), }; - payment_agent_store = new PaymentAgentStore({ root_store, WS }); + payment_agent_store = new PaymentAgentStore(WS as unknown as TWebSocket, root_store as TRootStore); }); beforeAll(() => { - const spyWindow = jest.spyOn(window, 'window', 'get'); - spyWindow.mockImplementation(() => ({ - location: { - pathname: routes.cashier_pa, - }, - })); + spyWindow = jest.spyOn(window, 'window', 'get'); + spyWindow.mockImplementation( + () => + ({ + location: { + pathname: routes.cashier_pa, + }, + } as Window & typeof globalThis) + ); }); afterAll(() => { - spyWindow.mockRestore(); + (spyWindow as jest.Mock).mockRestore(); }); it('should set active_tab_index', () => { @@ -158,7 +163,7 @@ describe('PaymentAgentStore', () => { }); it('should clear the list of supported banks', () => { - payment_agent_store.clearSuppertedBanks(); + payment_agent_store.clearSupportedBanks(); expect(payment_agent_store.supported_banks.length).toBe(0); }); @@ -176,24 +181,20 @@ describe('PaymentAgentStore', () => { it('should add payment agent to list', () => { payment_agent_store.setList(mocked_payment_agents[0]); - expect(payment_agent_store.list).toEqual( - expect.arrayContaining([ - { - currency: 'USD', - deposit_commission: 0, - email: 'pa@example.com', - further_information: 'further information', - max_withdrawal: '2000', - min_withdrawal: '10', - name: 'Payment Agent of CR90000000', - paymentagent_loginid: 'CR90000000', - phones: [{ phone_number: '+12345678' }], - supported_banks: [{ payment_method: 'Visa' }], - urls: [{ url: 'http://www.pa.com' }], - withdrawal_commission: 0, - }, - ]) - ); + expect(payment_agent_store.list).toContainEqual({ + currency: 'USD', + deposit_commission: '0', + email: 'pa@example.com', + further_information: 'further information', + max_withdrawal: '2000', + min_withdrawal: '10', + name: 'Payment Agent of CR90000000', + paymentagent_loginid: 'CR90000000', + phone_numbers: [{ phone_number: '+12345678' }], + supported_banks: [{ payment_method: 'Visa' }], + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', + }); }); it('should clear the list of payment agents', () => { @@ -205,45 +206,68 @@ describe('PaymentAgentStore', () => { const spySortSupportedBanks = jest.spyOn(payment_agent_store, 'sortSupportedBanks'); await payment_agent_store.setPaymentAgentList(); - expect(payment_agent_store.list).toEqual(expect.arrayContaining(mocked_payment_agents)); + expect(payment_agent_store.list).toContainEqual({ + currency: 'USD', + deposit_commission: '0', + email: 'pa@example.com', + further_information: 'further information', + max_withdrawal: '2000', + min_withdrawal: '10', + name: 'Payment Agent of CR90000000', + paymentagent_loginid: 'CR90000000', + phone_numbers: [{ phone_number: '+12345678' }], + supported_banks: [{ payment_method: 'Visa' }], + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', + }); + expect(payment_agent_store.list).toContainEqual({ + currency: 'USD', + deposit_commission: '0', + email: 'pa@example.com', + further_information: 'further information', + max_withdrawal: '2000', + min_withdrawal: '10', + name: 'Payment Agent of CR90000002', + paymentagent_loginid: 'CR90000002', + phone_numbers: [{ phone_number: '+12345678' }], + supported_banks: [{ payment_method: 'Visa' }, { payment_method: 'Mastercard' }], + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', + }); expect(spySortSupportedBanks).toHaveBeenCalled(); }); it('should filter payment agent list by selected bank', async () => { await payment_agent_store.setPaymentAgentList(); payment_agent_store.filterPaymentAgentList('card'); - expect(payment_agent_store.filtered_list).toEqual( - expect.arrayContaining([ - { - currency: 'USD', - deposit_commission: 0, - email: 'pa@example.com', - further_information: 'further information', - max_withdrawal: '2000', - min_withdrawal: '10', - name: 'Payment Agent of CR90000002', - paymentagent_loginid: 'CR90000002', - phones: [{ phone_number: '+12345678' }], - supported_banks: [{ payment_method: 'Visa' }, { payment_method: 'Mastercard' }], - urls: [{ url: 'http://www.pa.com' }], - withdrawal_commission: 0, - }, - { - currency: 'USD', - deposit_commission: 0, - email: 'pa@example.com', - further_information: 'further information', - max_withdrawal: '2000', - min_withdrawal: '10', - name: 'Payment Agent of CR90000000', - paymentagent_loginid: 'CR90000000', - phones: [{ phone_number: '+12345678' }], - supported_banks: [{ payment_method: 'Visa' }], - urls: [{ url: 'http://www.pa.com' }], - withdrawal_commission: 0, - }, - ]) - ); + expect(payment_agent_store.filtered_list).toContainEqual({ + currency: 'USD', + deposit_commission: '0', + email: 'pa@example.com', + further_information: 'further information', + max_withdrawal: '2000', + min_withdrawal: '10', + name: 'Payment Agent of CR90000000', + paymentagent_loginid: 'CR90000000', + phone_numbers: [{ phone_number: '+12345678' }], + supported_banks: [{ payment_method: 'Visa' }], + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', + }); + expect(payment_agent_store.filtered_list).toContainEqual({ + currency: 'USD', + deposit_commission: '0', + email: 'pa@example.com', + further_information: 'further information', + max_withdrawal: '2000', + min_withdrawal: '10', + name: 'Payment Agent of CR90000002', + paymentagent_loginid: 'CR90000002', + phone_numbers: [{ phone_number: '+12345678' }], + supported_banks: [{ payment_method: 'Visa' }, { payment_method: 'Mastercard' }], + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', + }); }); it('should filter payment agent list by search term', async () => { @@ -251,24 +275,20 @@ describe('PaymentAgentStore', () => { await payment_agent_store.setPaymentAgentList(); payment_agent_store.filterPaymentAgentList(); expect(payment_agent_store.filtered_list.length).toBe(1); - expect(payment_agent_store.filtered_list).toEqual( - expect.arrayContaining([ - { - currency: 'USD', - deposit_commission: 0, - email: 'pa@example.com', - further_information: 'further information', - max_withdrawal: '2000', - min_withdrawal: '10', - name: 'Payment Agent of CR90000002', - paymentagent_loginid: 'CR90000002', - phones: [{ phone_number: '+12345678' }], - supported_banks: [{ payment_method: 'Visa' }, { payment_method: 'Mastercard' }], - urls: [{ url: 'http://www.pa.com' }], - withdrawal_commission: 0, - }, - ]) - ); + expect(payment_agent_store.filtered_list).toContainEqual({ + currency: 'USD', + deposit_commission: '0', + email: 'pa@example.com', + further_information: 'further information', + max_withdrawal: '2000', + min_withdrawal: '10', + name: 'Payment Agent of CR90000002', + paymentagent_loginid: 'CR90000002', + phone_numbers: [{ phone_number: '+12345678' }], + supported_banks: [{ payment_method: 'Visa' }, { payment_method: 'Mastercard' }], + urls: [{ url: 'https://www.pa.com' }], + withdrawal_commission: '0', + }); }); it('should set has_payment_agent_search_warning to true when there is no matches for the search term', async () => { @@ -319,7 +339,7 @@ describe('PaymentAgentStore', () => { const spySetErrorMessage = jest.spyOn(payment_agent_store.error, 'setErrorMessage'); payment_agent_store.setIsTryWithdrawSuccessful(true); - expect(spySetErrorMessage).toHaveBeenCalledWith(''); + expect(spySetErrorMessage).toHaveBeenCalledWith({ code: '', message: '' }); expect(payment_agent_store.is_try_withdraw_successful).toBeTruthy(); }); @@ -330,7 +350,7 @@ describe('PaymentAgentStore', () => { it('should set confirm value', () => { const confirmation = { - amount: '100', + amount: 100, currency: 'USD', loginid: 'CR90000000', payment_agent_name: 'Payment Agent of CR90000000', @@ -346,8 +366,8 @@ describe('PaymentAgentStore', () => { payment_agent_email: 'pa@example.com', payment_agent_id: 'CR90000000', payment_agent_name: 'Payment Agent of CR90000000', - payment_agent_phone: '+12345678', - payment_agent_url: 'http://www.pa.com', + payment_agent_phone: [{ phone_number: '+12345678' }], + payment_agent_url: [{ url: 'https://www.pa.com' }], }; payment_agent_store.setReceipt(receipt); @@ -362,23 +382,19 @@ describe('PaymentAgentStore', () => { min_withdrawal: '10', email: 'pa@example.com', phone_numbers: [{ phone_number: '+12345678' }], - urls: [{ url: 'http://www.pa.com' }], + urls: [{ url: 'https://www.pa.com' }], }; payment_agent_store.addPaymentAgent(payment_agent); - expect(payment_agent_store.agents).toEqual( - expect.arrayContaining([ - { - text: 'Payment Agent of CR90000003', - value: 'CR90000003', - max_withdrawal: '2000', - min_withdrawal: '10', - email: 'pa@example.com', - phone: [{ phone_number: '+12345678' }], - url: [{ url: 'http://www.pa.com' }], - }, - ]) - ); + expect(payment_agent_store.agents).toContainEqual({ + text: 'Payment Agent of CR90000003', + value: 'CR90000003', + max_withdrawal: '2000', + min_withdrawal: '10', + email: 'pa@example.com', + phone_numbers: [{ phone_number: '+12345678' }], + url: [{ url: 'https://www.pa.com' }], + }); }); it('should mount payment agent withdraw', async () => { @@ -387,23 +403,21 @@ describe('PaymentAgentStore', () => { expect(payment_agent_store.is_withdraw).toBeTruthy(); expect(payment_agent_store.is_withdraw_successful).toBeFalsy(); expect(payment_agent_store.receipt).toEqual({}); - expect(payment_agent_store.agents).toEqual( - expect.arrayContaining([ - { - text: 'Payment Agent of CR90000000', - value: 'CR90000000', - max_withdrawal: '2000', - min_withdrawal: '10', - email: 'pa@example.com', - phone: [{ phone_number: '+12345678' }], - url: [{ url: 'http://www.pa.com' }], - }, - ]) - ); + expect(payment_agent_store.agents).toContainEqual({ + text: 'Payment Agent of CR90000000', + value: 'CR90000000', + max_withdrawal: '2000', + min_withdrawal: '10', + email: 'pa@example.com', + phone_numbers: [{ phone_number: '+12345678' }], + url: [{ url: 'https://www.pa.com' }], + }); }); it('should redirect to deposit page if there is no available payment agent upon accessing PA withdrawal', async () => { - payment_agent_store.WS.authorized.paymentAgentList.mockResolvedValueOnce({ paymentagent_list: { list: [] } }); + (payment_agent_store.WS.authorized.paymentAgentList as jest.Mock).mockResolvedValueOnce({ + paymentagent_list: { list: [] }, + }); await payment_agent_store.onMountPaymentAgentWithdraw(); expect(payment_agent_store.root_store.common.routeTo).toHaveBeenCalledWith(routes.cashier_deposit); }); @@ -413,9 +427,9 @@ describe('PaymentAgentStore', () => { await payment_agent_store.onMountPaymentAgentWithdraw(); await payment_agent_store.requestTryPaymentAgentWithdraw(mocked_withdrawal_request); - expect(spySetErrorMessage).toHaveBeenCalledWith(''); + expect(spySetErrorMessage).toHaveBeenCalledWith({ code: '', message: '' }); expect(payment_agent_store.confirm).toEqual({ - amount: '200', + amount: 200, currency: 'USD', loginid: 'CR90000000', payment_agent_name: 'Payment Agent of CR90000000', @@ -427,7 +441,9 @@ describe('PaymentAgentStore', () => { const spySetErrorMessage = jest.spyOn(payment_agent_store.error, 'setErrorMessage'); const error_message = { message: 'Sorry, an error occurred.' }; - payment_agent_store.WS.authorized.paymentAgentWithdraw.mockResolvedValueOnce({ error: error_message }); + (payment_agent_store.WS.authorized.paymentAgentWithdraw as jest.Mock).mockResolvedValueOnce({ + error: error_message, + }); await payment_agent_store.requestTryPaymentAgentWithdraw(mocked_withdrawal_request); expect(spySetErrorMessage).toHaveBeenLastCalledWith(error_message, payment_agent_store.resetPaymentAgent); expect(payment_agent_store.is_try_withdraw_successful).toBeFalsy(); @@ -437,7 +453,7 @@ describe('PaymentAgentStore', () => { // const spySetErrorMessage = jest.spyOn(payment_agent_store.error, 'setErrorMessage'); // payment_agent_store.resetPaymentAgent(); - // expect(spySetErrorMessage).toHaveBeenLastCalledWith(''); + // expect(spySetErrorMessage).toHaveBeenLastCalledWith({ code: '', message: '' }); // expect(payment_agent_store.is_withdraw).toBeFalsy(); // expect(payment_agent_store.active_tab_index).toBe(0); // }); @@ -450,7 +466,9 @@ describe('PaymentAgentStore', () => { }); it('should request for payment agent withdraw', async () => { - payment_agent_store.WS.authorized.paymentAgentWithdraw.mockResolvedValueOnce({ paymentagent_withdraw: 1 }); + (payment_agent_store.WS.authorized.paymentAgentWithdraw as jest.Mock).mockResolvedValueOnce({ + paymentagent_withdraw: 1, + }); await payment_agent_store.onMountPaymentAgentWithdraw(); await payment_agent_store.requestPaymentAgentWithdraw(mocked_withdrawal_request); expect(payment_agent_store.receipt).toEqual({ @@ -459,7 +477,7 @@ describe('PaymentAgentStore', () => { payment_agent_id: 'CR90000000', payment_agent_name: 'Payment Agent of CR90000000', payment_agent_phone: [{ phone_number: '+12345678' }], - payment_agent_url: [{ url: 'http://www.pa.com' }], + payment_agent_url: [{ url: 'https://www.pa.com' }], }); expect(payment_agent_store.is_withdraw_successful).toBeTruthy(); expect(payment_agent_store.is_try_withdraw_successful).toBeFalsy(); @@ -470,7 +488,9 @@ describe('PaymentAgentStore', () => { const spySetErrorMessage = jest.spyOn(payment_agent_store.error, 'setErrorMessage'); const error_message = { message: 'Sorry, an error occurred.' }; - payment_agent_store.WS.authorized.paymentAgentWithdraw.mockResolvedValueOnce({ error: error_message }); + (payment_agent_store.WS.authorized.paymentAgentWithdraw as jest.Mock).mockResolvedValueOnce({ + error: error_message, + }); await payment_agent_store.requestPaymentAgentWithdraw(mocked_withdrawal_request); expect(spySetErrorMessage).toHaveBeenLastCalledWith(error_message, payment_agent_store.resetPaymentAgent); expect(payment_agent_store.is_withdraw_successful).toBeFalsy(); diff --git a/packages/cashier/src/stores/__tests__/payment-agent-transfer-store.spec.js b/packages/cashier/src/stores/__tests__/payment-agent-transfer-store.spec.ts similarity index 72% rename from packages/cashier/src/stores/__tests__/payment-agent-transfer-store.spec.js rename to packages/cashier/src/stores/__tests__/payment-agent-transfer-store.spec.ts index 4c8ccbf0480e..7390bb55a13f 100644 --- a/packages/cashier/src/stores/__tests__/payment-agent-transfer-store.spec.js +++ b/packages/cashier/src/stores/__tests__/payment-agent-transfer-store.spec.ts @@ -1,10 +1,22 @@ import PaymentAgentTransferStore from '../payment-agent-transfer-store'; -import { routes } from '@deriv/shared'; import { configure } from 'mobx'; +import { mockStore } from '@deriv/stores'; +import { PaymentAgentListResponse } from '@deriv/api-types'; +import { TWebSocket, TRootStore } from '../../types'; configure({ safeDescriptors: false }); -let payment_agent_transfer_store, response_payment_agent, root_store, transfer_data, WS; +let payment_agent_transfer_store: PaymentAgentTransferStore, + response_payment_agent: PaymentAgentListResponse, + root_store: TRootStore, + transfer_data: { + transfer_to: string; + amount: number; + description: string; + currency: string; + dry_run?: 0 | 1; + }, + WS: TWebSocket; beforeEach(() => { WS = { @@ -15,7 +27,7 @@ beforeEach(() => { paymentAgentTransfer: jest.fn(), }, }; - root_store = { + root_store = mockStore({ common: { routeTo: jest.fn(), }, @@ -42,8 +54,8 @@ beforeEach(() => { ui: { is_real_acc_signup_on: false, }, - }; - payment_agent_transfer_store = new PaymentAgentTransferStore({ WS, root_store }); + }) as TRootStore; + payment_agent_transfer_store = new PaymentAgentTransferStore(WS, root_store); response_payment_agent = { paymentagent_list: { list: [{ paymentagent_loginid: 'CR9000000' }], @@ -58,22 +70,22 @@ beforeEach(() => { }); describe('PaymentAgentTransferStore', () => { - it('shoud clear an error and set correct is_try_transfer_successful value', () => { + it('should clear an error and set correct is_try_transfer_successful value', () => { const spySetErrorMessage = jest.spyOn(payment_agent_transfer_store.error, 'setErrorMessage'); payment_agent_transfer_store.setIsTryTransferSuccessful(true); - expect(spySetErrorMessage).toHaveBeenCalledWith(''); + expect(spySetErrorMessage).toHaveBeenCalledWith({ code: '', message: '' }); expect(payment_agent_transfer_store.is_try_transfer_successful).toBeTruthy(); }); - it('shoud set correct is_transfer_successful value', () => { + it('should set correct is_transfer_successful value', () => { payment_agent_transfer_store.setIsTransferSuccessful(true); expect(payment_agent_transfer_store.is_transfer_successful).toBeTruthy(); }); - it('shoud set correct confirmation transfer value', () => { + it('should set correct confirmation transfer value', () => { const confirm = { amount: 100, client_id: 'CR9000000', @@ -86,7 +98,7 @@ describe('PaymentAgentTransferStore', () => { expect(payment_agent_transfer_store.confirm).toEqual(confirm); }); - it('shoud set correct receipt value', () => { + it('should set correct receipt value', () => { const receipt = { amount_transferred: 100, client_id: 'CR9000000', @@ -98,7 +110,7 @@ describe('PaymentAgentTransferStore', () => { expect(payment_agent_transfer_store.receipt).toEqual(receipt); }); - it('shoud set correct transfer_limit value', () => { + it('should set correct transfer_limit value', () => { const transfer_limit = { min_withdrawal: 1, max_withdrawal: 10, @@ -107,17 +119,17 @@ describe('PaymentAgentTransferStore', () => { payment_agent_transfer_store.setMinMaxPaymentAgentTransfer(transfer_limit); expect(payment_agent_transfer_store.transfer_limit).toEqual({ - min: transfer_limit.min_withdrawal, - max: transfer_limit.max_withdrawal, + min_withdrawal: transfer_limit.min_withdrawal, + max_withdrawal: transfer_limit.max_withdrawal, }); }); - it('shoud reset payment agent transfer', () => { + it('should reset payment agent transfer', () => { const spySetErrorMessage = jest.spyOn(payment_agent_transfer_store.error, 'setErrorMessage'); payment_agent_transfer_store.resetPaymentAgentTransfer(); expect(payment_agent_transfer_store.is_transfer_successful).toBeFalsy(); - expect(spySetErrorMessage).toHaveBeenCalledWith(''); + expect(spySetErrorMessage).toHaveBeenCalledWith({ code: '', message: '' }); }); it('should get current payment agent from response_payment_agent', async () => { @@ -129,7 +141,9 @@ describe('PaymentAgentTransferStore', () => { }); it('should get current payment agent from cashier.payment_agent.getPaymentAgentDetails()', async () => { - payment_agent_transfer_store.root_store.modules.cashier.payment_agent.getPaymentAgentDetails.mockResolvedValue({ + ( + payment_agent_transfer_store.root_store.modules.cashier.payment_agent.getPaymentAgentDetails as jest.Mock + ).mockResolvedValue({ paymentagent_loginid: 'CR9999999', }); @@ -145,10 +159,9 @@ describe('PaymentAgentTransferStore', () => { it('should mount payment agent transfer', async () => { await payment_agent_transfer_store.onMountPaymentAgentTransfer(); - expect(payment_agent_transfer_store.root_store.modules.cashier.general_store.setLoading.mock.calls).toEqual([ - [true], - [false], - ]); + expect( + (payment_agent_transfer_store.root_store.modules.cashier.general_store.setLoading as jest.Mock).mock.calls + ).toEqual([[true], [false]]); expect( payment_agent_transfer_store.root_store.modules.cashier.general_store.onMountCommon ).toHaveBeenCalledTimes(1); @@ -160,13 +173,13 @@ describe('PaymentAgentTransferStore', () => { await payment_agent_transfer_store.onMountPaymentAgentTransfer(); expect(payment_agent_transfer_store.transfer_limit).toEqual({ - min: 1, - max: 10, + min_withdrawal: 1, + max_withdrawal: 10, }); }); - it('shoud set correct confirmation transfer value if there is no any errors in response (dry_run = 1)', async () => { - payment_agent_transfer_store.WS.authorized.paymentAgentTransfer.mockResolvedValue({ + it('should set correct confirmation transfer value if there is no any errors in response (dry_run = 1)', async () => { + (payment_agent_transfer_store.WS.authorized.paymentAgentTransfer as jest.Mock).mockResolvedValue({ paymentagent_transfer: 2, client_to_full_name: 'George', }); @@ -182,9 +195,9 @@ describe('PaymentAgentTransferStore', () => { expect(payment_agent_transfer_store.is_try_transfer_successful).toBeTruthy(); }); - it('shoud trigger setErrorMessage callback if there is an error in response (paymentagent_transfer = 0), requestTryPaymentAgentTransfer', async () => { + it('should trigger setErrorMessage callback if there is an error in response (paymentagent_transfer = 0), requestTryPaymentAgentTransfer', async () => { const spySetErrorMessage = jest.spyOn(payment_agent_transfer_store.error, 'setErrorMessage'); - payment_agent_transfer_store.WS.authorized.paymentAgentTransfer.mockResolvedValue({ + (payment_agent_transfer_store.WS.authorized.paymentAgentTransfer as jest.Mock).mockResolvedValue({ paymentagent_transfer: 0, error: { message: 'Error message!', @@ -199,8 +212,8 @@ describe('PaymentAgentTransferStore', () => { ); }); - it('shoud set correct confirmation transfer value if there is no any errors in response (dry_run = 0)', async () => { - payment_agent_transfer_store.WS.authorized.paymentAgentTransfer.mockResolvedValue({ + it('should set correct confirmation transfer value if there is no any errors in response (dry_run = 0)', async () => { + (payment_agent_transfer_store.WS.authorized.paymentAgentTransfer as jest.Mock).mockResolvedValue({ paymentagent_transfer: 1, client_to_full_name: 'George', }); @@ -217,9 +230,9 @@ describe('PaymentAgentTransferStore', () => { expect(payment_agent_transfer_store.confirm).toEqual({}); }); - it('shoud trigger setErrorMessage callback if there is an error in response (paymentagent_transfer = 0), requestPaymentAgentTransfer', async () => { + it('should trigger setErrorMessage callback if there is an error in response (paymentagent_transfer = 0), requestPaymentAgentTransfer', async () => { const spySetErrorMessage = jest.spyOn(payment_agent_transfer_store.error, 'setErrorMessage'); - payment_agent_transfer_store.WS.authorized.paymentAgentTransfer.mockResolvedValue({ + (payment_agent_transfer_store.WS.authorized.paymentAgentTransfer as jest.Mock).mockResolvedValue({ paymentagent_transfer: 0, error: { message: 'Error message!', diff --git a/packages/cashier/src/stores/__tests__/transaction-history-store.spec.ts b/packages/cashier/src/stores/__tests__/transaction-history-store.spec.ts index d6a8fc5f971b..28daca99d570 100644 --- a/packages/cashier/src/stores/__tests__/transaction-history-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/transaction-history-store.spec.ts @@ -1,6 +1,7 @@ import TransactionHistoryStore from '../transaction-history-store'; import { configure } from 'mobx'; -import { TRootStore, TWebSocket } from 'Types'; +import { TStatusCode, TTransactionType, TRootStore, TWebSocket } from '../../types'; +import { mockStore } from '@deriv/stores'; configure({ safeDescriptors: false }); @@ -13,19 +14,20 @@ describe('TransactionHistoryStore', () => { amount: 0.0005531, id: '175', is_valid_to_cancel: 1, - status_code: 'LOCKED', + status_code: 'LOCKED' as TStatusCode, status_message: 'We`re reviewing your withdrawal request. You may still cancel this transaction if you wish. Once we start processing, you won`t be able to cancel.', submit_date: 1648811322, - transaction_type: 'withdrawal', + transaction_hash: '0x2dbf00eb6dbbcb1a962d7f1c82b8cff889a579f23af417a8b62442dd49bd8a51', + transaction_type: 'withdrawal' as TTransactionType, }, ]; - const root_store: DeepPartial = { + const root_store = mockStore({ client: { currency: 'BTC', switched: false, }, - }; + }); const WS: DeepPartial = { authorized: { cashierPayments: () => @@ -54,7 +56,7 @@ describe('TransactionHistoryStore', () => { it('should subscribe to crypto transactions', async () => { const spyUpdateCryptoTransactions = jest.spyOn(transaction_history_store, 'updateCryptoTransactions'); - transaction_history_store.getCryptoTransactions(); + await transaction_history_store.getCryptoTransactions(); expect(spyUpdateCryptoTransactions).toHaveBeenCalledWith(crypto_transactions); expect(transaction_history_store.crypto_transactions).toEqual(crypto_transactions); }); @@ -68,10 +70,11 @@ describe('TransactionHistoryStore', () => { amount: 0.0005531, id: '175', is_valid_to_cancel: 0, - status_code: 'CANCELLED', + status_code: 'CANCELLED' as TStatusCode, status_message: 'You’ve cancelled your withdrawal request.', submit_date: 1649048412, - transaction_type: 'withdrawal', + transaction_hash: '0x2dbf00eb6dbbcb1a962d7f1c82b8cff889a579f23af417a8b62442dd49bd8a51', + transaction_type: 'withdrawal' as TTransactionType, }, { address_hash: 'tb1ql7w62elx9ucw4pj1lgw4l028hmuw80sndtntxt', @@ -80,11 +83,12 @@ describe('TransactionHistoryStore', () => { amount: 0.0005531, id: '176', is_valid_to_cancel: 1, - status_code: 'LOCKED', + status_code: 'LOCKED' as TStatusCode, status_message: 'We`re reviewing your withdrawal request. You may still cancel this transaction if you wish. Once we start processing, you won`t be able to cancel.', submit_date: 1649048412, - transaction_type: 'withdrawal', + transaction_hash: '0x2dbf00eb6dbbcb1a962d7f1c82b8cff889a579f23af417a8b62442dd49bd8a51', + transaction_type: 'withdrawal' as TTransactionType, }, ]; diff --git a/packages/cashier/src/stores/__tests__/withdraw-store.spec.ts b/packages/cashier/src/stores/__tests__/withdraw-store.spec.ts index 46b92f03faa3..c9f9a1d63df0 100644 --- a/packages/cashier/src/stores/__tests__/withdraw-store.spec.ts +++ b/packages/cashier/src/stores/__tests__/withdraw-store.spec.ts @@ -1,7 +1,8 @@ import { isMobile, validNumber } from '@deriv/shared'; import WithdrawStore from '../withdraw-store'; import { configure } from 'mobx'; -import { TWebSocket, TRootStore } from 'Types'; +import { TWebSocket, TRootStore } from '../../types'; +import { mockStore } from '@deriv/stores'; configure({ safeDescriptors: false }); @@ -15,10 +16,10 @@ jest.mock('@deriv/shared', () => ({ })); describe('WithdrawStore', () => { - let withdraw_store: WithdrawStore, root_store: DeepPartial, WS: DeepPartial; + let withdraw_store: WithdrawStore, root_store: TRootStore, WS: DeepPartial; beforeEach(() => { - root_store = { + root_store = mockStore({ client: { account_list: [ { @@ -82,7 +83,7 @@ describe('WithdrawStore', () => { }, }, }, - }; + }) as TRootStore; WS = { authorized: { cashier: jest.fn(() => Promise.resolve({ cashier: 'https://deriv.com' })), @@ -91,7 +92,7 @@ describe('WithdrawStore', () => { cryptoWithdraw: jest.fn(() => Promise.resolve({})), }; - withdraw_store = new WithdrawStore(WS as TWebSocket, root_store as TRootStore); + withdraw_store = new WithdrawStore(WS as TWebSocket, root_store); }); it('should set is_withdraw_confirmed', () => { @@ -201,7 +202,6 @@ describe('WithdrawStore', () => { const verification_code = 'aBcDefXa'; withdraw_store.root_store.modules.cashier.iframe.is_session_timeout = true; - withdraw_store.root_store.modules.cashier.general_store.is_crypto = true; await withdraw_store.onMountWithdraw(verification_code); expect(setIframeUrl).toHaveBeenCalledWith(''); @@ -240,6 +240,7 @@ describe('WithdrawStore', () => { prompt_client_to_authenticate: 0, risk_classification: '', status: [], + p2p_status: 'none', }; expect(withdraw_store.is_withdrawal_locked).toBeFalsy(); }); @@ -282,11 +283,11 @@ describe('WithdrawStore', () => { const { percentageSelectorSelectionStatus } = withdraw_store.root_store.modules.cashier.general_store; const spyValidateWithdrawFromAmount = jest.spyOn(withdraw_store, 'validateWithdrawFromAmount'); - withdraw_store.setWithdrawPercentageSelectorResult(100); - expect(setConverterFromAmount).toHaveBeenCalledWith(100); + withdraw_store.setWithdrawPercentageSelectorResult('100'); + expect(setConverterFromAmount).toHaveBeenCalledWith('100'); expect(spyValidateWithdrawFromAmount).toHaveBeenCalled(); - withdraw_store.setWithdrawPercentageSelectorResult(0); + withdraw_store.setWithdrawPercentageSelectorResult('0'); expect(resetConverter).toHaveBeenCalled(); expect(setIsTimerVisible).toHaveBeenCalledWith(false); expect(percentageSelectorSelectionStatus).toHaveBeenCalledWith(false); @@ -304,11 +305,11 @@ describe('WithdrawStore', () => { const { setConverterFromError } = withdraw_store.root_store.modules.cashier.crypto_fiat_converter; withdraw_store.crypto_config = { currencies_config: { USD: { minimum_withdrawal: 2000 } } }; - isMobile.mockReturnValueOnce(true); + (isMobile as jest.Mock).mockReturnValueOnce(true); withdraw_store.validateWithdrawFromAmount(); expect(setConverterFromError).toHaveBeenCalled(); - isMobile.mockReturnValueOnce(false); + (isMobile as jest.Mock).mockReturnValueOnce(false); withdraw_store.validateWithdrawFromAmount(); expect(setConverterFromError).toHaveBeenCalledWith( 'Your balance (1,000.00 USD) is less than the current minimum withdrawal allowed (2,000.00 USD). Please top up your account to continue with your withdrawal.' @@ -320,7 +321,7 @@ describe('WithdrawStore', () => { withdraw_store.root_store.modules.cashier.crypto_fiat_converter; const error_message = 'Should be a valid number.'; - validNumber.mockReturnValue({ is_ok: false, message: error_message }); + (validNumber as jest.Mock).mockReturnValue({ is_ok: false, message: error_message }); withdraw_store.validateWithdrawFromAmount(); expect(setConverterFromError).toHaveBeenCalledWith(error_message); diff --git a/packages/cashier/src/stores/account-prompt-dialog-store.ts b/packages/cashier/src/stores/account-prompt-dialog-store.ts index 7cb0a8257f9d..b18da9808383 100644 --- a/packages/cashier/src/stores/account-prompt-dialog-store.ts +++ b/packages/cashier/src/stores/account-prompt-dialog-store.ts @@ -1,6 +1,6 @@ import { observable, action, makeObservable } from 'mobx'; import { isCryptocurrency } from '@deriv/shared'; -import type { TRootStore } from 'Types'; +import { TRootStore } from '../types'; export default class AccountPromptDialogStore { constructor(public root_store: TRootStore) { diff --git a/packages/cashier/src/stores/account-transfer-store.ts b/packages/cashier/src/stores/account-transfer-store.ts index fb6b9bd24175..e5f7e5244c85 100644 --- a/packages/cashier/src/stores/account-transfer-store.ts +++ b/packages/cashier/src/stores/account-transfer-store.ts @@ -70,7 +70,7 @@ export default class AccountTransferStore { }); } - accounts_list: Array = []; + accounts_list: TAccount[] = []; container: string = Constants.containers.account_transfer; error = new ErrorStore(); has_no_account = false; @@ -446,7 +446,7 @@ export default class AccountTransferStore { platform_icon: account.account_type === CFD_PLATFORMS.MT5 && combined_cfd_mt5_account ? combined_cfd_mt5_account.icon - : cfd_icon_display, + : (cfd_icon_display as TPlatformIcon), status: account?.status, market_type: getCFDAccount({ market_type: account.market_type, @@ -697,7 +697,7 @@ export default class AccountTransferStore { }); if (!is_ok) { setConverterFromError(message || ''); - } else if (Number(this.selected_from.balance) < +converter_from_amount) { + } else if (Number(this.selected_from.balance) < Number(converter_from_amount)) { setConverterFromError(localize('Insufficient funds')); } else { setConverterFromError(''); diff --git a/packages/cashier/src/stores/base-store.ts b/packages/cashier/src/stores/base-store.ts index e1d9b7c9a0ba..2a844e08b085 100644 --- a/packages/cashier/src/stores/base-store.ts +++ b/packages/cashier/src/stores/base-store.ts @@ -1,23 +1,22 @@ import { action, intercept, observable, reaction, toJS, when, makeObservable } from 'mobx'; -import { isProduction, isEmptyObject } from '@deriv/shared'; -import Validator from 'Utils/validator/validator'; -import type { TRootStore } from 'Types'; +import { isProduction, isEmptyObject, Validator } from '@deriv/shared'; +import { TRootStore } from '../types'; type TListenerResponse = { then: (func: VoidFunction) => void; }; -type TValidationRules = { [key: string]: Array | string } & { +type TValidationRules = { [key: string]: string[] | string } & { [key: string]: { trigger?: PropertyKey; - rules?: Array | string; + rules?: string[] | string; }; }; type TBaseStoreOptions = { root_store?: TRootStore; - local_storage_properties?: Array; - session_storage_properties?: Array; + local_storage_properties?: string[]; + session_storage_properties?: string[]; validation_rules?: TValidationRules; store_name?: string; }; @@ -38,7 +37,7 @@ export default class BaseStore { client_init_listener: null | (() => TListenerResponse) = null; clientInitDisposer: null | (() => void) = null; - local_storage_properties?: Array; + local_storage_properties?: string[]; logout_listener: null | (() => TListenerResponse) = null; logoutDisposer: null | (() => void) = null; network_status_change_listener: null | ((is_online?: boolean) => TListenerResponse) = null; @@ -49,7 +48,7 @@ export default class BaseStore { real_account_signup_ended_listener: null | (() => TListenerResponse) = null; realAccountSignupEndedDisposer: null | (() => void) = null; root_store?: TRootStore; - session_storage_properties?: Array; + session_storage_properties?: string[]; store_name = ''; switch_account_listener: null | (() => TListenerResponse) = null; switchAccountDisposer: null | (() => void) = null; @@ -548,7 +547,7 @@ export default class BaseStore { this.disposeRealAccountSignupEnd(); } - assertHasValidCache(loginid: string, ...reactions: Array<() => void>): void { + assertHasValidCache(loginid: string, ...reactions: VoidFunction[]): void { // account was changed when this was unmounted. if (this.root_store?.client.loginid !== loginid) { reactions.forEach(act => act()); diff --git a/packages/cashier/src/stores/cashier-store.ts b/packages/cashier/src/stores/cashier-store.ts index d7ac707cc32e..988356a65784 100644 --- a/packages/cashier/src/stores/cashier-store.ts +++ b/packages/cashier/src/stores/cashier-store.ts @@ -38,8 +38,8 @@ export default class CashierStore { this.general_store = new GeneralStore(WS, root_store); this.iframe = new IframeStore(root_store); this.onramp = new OnRampStore(WS, root_store); - this.payment_agent = new PaymentAgentStore({ root_store, WS }); - this.payment_agent_transfer = new PaymentAgentTransferStore({ root_store, WS }); + this.payment_agent = new PaymentAgentStore(WS, root_store); + this.payment_agent_transfer = new PaymentAgentTransferStore(WS, root_store); this.transaction_history = new TransactionHistoryStore(WS, root_store); this.withdraw = new WithdrawStore(WS, root_store); } diff --git a/packages/cashier/src/stores/crypto-fiat-converter-store.ts b/packages/cashier/src/stores/crypto-fiat-converter-store.ts index c02f7edd09d7..113b6db4dd5d 100644 --- a/packages/cashier/src/stores/crypto-fiat-converter-store.ts +++ b/packages/cashier/src/stores/crypto-fiat-converter-store.ts @@ -1,6 +1,6 @@ import { action, observable, makeObservable } from 'mobx'; import { getDecimalPlaces } from '@deriv/shared'; -import { TRootStore, TWebSocket } from 'Types'; +import { TRootStore, TWebSocket } from '../types'; export default class CryptoFiatConverterStore { constructor(public WS: TWebSocket, public root_store: TRootStore) { diff --git a/packages/cashier/src/stores/error-store.ts b/packages/cashier/src/stores/error-store.ts index a924e9a5eb43..a62f05c13fe7 100644 --- a/packages/cashier/src/stores/error-store.ts +++ b/packages/cashier/src/stores/error-store.ts @@ -26,7 +26,7 @@ export default class ErrorStore { message = ''; code = ''; - fields = ''; + fields: string | string[] = ''; is_show_full_page = false; onClickButton: VoidFunction | null = null; is_ask_uk_funds_protection = false; diff --git a/packages/cashier/src/stores/general-store.ts b/packages/cashier/src/stores/general-store.ts index b882942e165c..ba7718d7bdad 100644 --- a/packages/cashier/src/stores/general-store.ts +++ b/packages/cashier/src/stores/general-store.ts @@ -3,7 +3,7 @@ import { isCryptocurrency, routes } from '@deriv/shared'; import Constants from 'Constants/constants'; import BaseStore from './base-store'; import PaymentAgentStore from './payment-agent-store'; -import type { TRootStore, TWebSocket } from 'Types'; +import type { TRootStore, TWebSocket } from '../types'; export default class GeneralStore extends BaseStore { constructor(public WS: TWebSocket, public root_store: TRootStore) { @@ -58,7 +58,7 @@ export default class GeneralStore extends BaseStore { ); } - active_container = Constants.containers.deposit; + active_container: keyof typeof Constants.containers = Constants.containers.deposit; cashier_route_tab_index = 0; deposit_target = ''; has_set_currency = false; @@ -160,7 +160,7 @@ export default class GeneralStore extends BaseStore { if (is_logged_in) { if (!switched) { - payment_agent.setPaymentAgentList().then(payment_agent.filterPaymentAgentList); + payment_agent.setPaymentAgentList().then(() => payment_agent.filterPaymentAgentList()); // check if withdrawal limit is reached // if yes, this will trigger to show a notification await withdraw.check10kLimit(); @@ -224,7 +224,7 @@ export default class GeneralStore extends BaseStore { } setActiveTab(container: string): void { - this.active_container = container; + this.active_container = container as keyof typeof Constants.containers; } accountSwitcherListener() { diff --git a/packages/cashier/src/stores/on-ramp-store.ts b/packages/cashier/src/stores/on-ramp-store.ts index 2c3719d3bfec..1718133c0e8f 100644 --- a/packages/cashier/src/stores/on-ramp-store.ts +++ b/packages/cashier/src/stores/on-ramp-store.ts @@ -3,7 +3,7 @@ import { localize } from '@deriv/translations'; import { getKebabCase, isCryptocurrency, routes, websiteUrl } from '@deriv/shared'; import createBanxaProvider from '../pages/on-ramp/on-ramp-providers'; import BaseStore from './base-store'; -import type { TWebSocket, TRootStore, TOnRampProvider, TServerError } from 'Types'; +import type { TWebSocket, TRootStore, TOnRampProvider, TServerError } from '../types'; export default class OnRampStore extends BaseStore { constructor(public WS: TWebSocket, public root_store: TRootStore) { @@ -66,11 +66,7 @@ export default class OnRampStore extends BaseStore { get is_onramp_tab_visible() { const { client } = this.root_store; - return ( - client.is_virtual === false && - isCryptocurrency(client.currency) && - this.filtered_onramp_providers.length > 0 - ); + return !client.is_virtual && isCryptocurrency(client.currency) && this.filtered_onramp_providers.length > 0; } get filtered_onramp_providers() { @@ -124,10 +120,10 @@ export default class OnRampStore extends BaseStore { const script_name = `${getKebabCase(provider.name)}-onramp`; if (!loadjs.isDefined(script_name)) { loadjs(dependencies, script_name, { - error: () => { + error: async () => { // eslint-disable-next-line no-console console.warn(`Dependencies for onramp provider ${provider.name} could not be loaded.`); - this.setSelectedProvider(null); + await this.setSelectedProvider(null); }, }); } @@ -177,12 +173,12 @@ export default class OnRampStore extends BaseStore { this.setShouldShowWidget(true); } - onClickGoToDepositPage() { - this.pollApiForDepositAddress(false); + async onClickGoToDepositPage() { + await this.pollApiForDepositAddress(false); window.open(websiteUrl() + routes.cashier_deposit.substring(1)); } - pollApiForDepositAddress(should_allow_empty_address: boolean) { + async pollApiForDepositAddress(should_allow_empty_address: boolean) { // should_allow_empty_address: API returns empty deposit address for legacy accounts // that have never generated a deposit address. Setting this to "true" will allow // the user to be redirected to the Deposit page (where an address will be generated). @@ -192,8 +188,8 @@ export default class OnRampStore extends BaseStore { this.setApiError(null); const deposit_address_interval = setInterval(() => getDepositAddressFromApi, 3000); - const getDepositAddressFromApi = () => { - this.WS.authorized.cashier('deposit', { provider: 'crypto', type: 'api' }).then(response => { + const getDepositAddressFromApi = async () => { + await this.WS.authorized.cashier('deposit', { provider: 'crypto', type: 'api' }).then(response => { let should_clear_interval = false; if (response.error) { @@ -215,18 +211,18 @@ export default class OnRampStore extends BaseStore { }); }; - getDepositAddressFromApi(); + await getDepositAddressFromApi(); setTimeout(() => { clearInterval(deposit_address_interval); this.setIsDepositAddressLoading(false); }, 30000); } - resetPopup() { + async resetPopup() { this.setApiError(null); this.setDepositAddress(null); this.setIsDepositAddressLoading(true); - this.setSelectedProvider(null); + await this.setSelectedProvider(null); this.setShouldShowWidget(false); this.setWidgetError(null); this.setWidgetHtml(null); @@ -252,11 +248,11 @@ export default class OnRampStore extends BaseStore { this.is_requesting_widget_html = is_requesting_widget_html; } - setSelectedProvider(provider?: TOnRampProvider | null) { + async setSelectedProvider(provider?: TOnRampProvider | null) { if (provider) { this.selected_provider = provider; this.setIsOnRampModalOpen(true); - this.pollApiForDepositAddress(true); + await this.pollApiForDepositAddress(true); } else { this.setIsOnRampModalOpen(false); this.selected_provider = null; diff --git a/packages/cashier/src/stores/payment-agent-store.js b/packages/cashier/src/stores/payment-agent-store.ts similarity index 73% rename from packages/cashier/src/stores/payment-agent-store.js rename to packages/cashier/src/stores/payment-agent-store.ts index 99a0c3d76907..3769521db7f4 100644 --- a/packages/cashier/src/stores/payment-agent-store.js +++ b/packages/cashier/src/stores/payment-agent-store.ts @@ -1,11 +1,25 @@ -import { action, computed, observable, makeObservable } from 'mobx'; +import { action, computed, observable, makeObservable, IObservableArray } from 'mobx'; +import { PaymentAgentDetailsResponse, PaymentagentList } from '@deriv/api-types'; import { formatMoney, routes, shuffleArray } from '@deriv/shared'; import { getNormalizedPaymentMethod } from 'Utils/utility'; import Constants from 'Constants/constants'; import ErrorStore from './error-store'; +import { + TRootStore, + TWebSocket, + TAgent, + TPaymentAgent, + TPaymentAgentWithdrawConfirm, + TPaymentAgentWithdrawReceipt, + TPaymentAgentWithdrawRequest, + TSupportedBank, + TPartialPaymentAgentList, + TTarget, + TServerError, +} from '../types'; export default class PaymentAgentStore { - constructor({ WS, root_store }) { + constructor(public WS: TWebSocket, public root_store: TRootStore) { makeObservable(this, { list: observable, agents: observable, @@ -30,7 +44,7 @@ export default class PaymentAgentStore { getPaymentAgentList: action.bound, getPaymentAgentDetails: action.bound, addSupportedBank: action.bound, - clearSuppertedBanks: action.bound, + clearSupportedBanks: action.bound, sortSupportedBanks: action.bound, setList: action.bound, clearList: action.bound, @@ -51,38 +65,45 @@ export default class PaymentAgentStore { resetPaymentAgent: action.bound, onMountPaymentAgentList: action.bound, requestPaymentAgentWithdraw: action.bound, + onRemount: observable, + setOnRemount: action.bound, }); this.root_store = root_store; this.WS = WS; } - list = []; - agents = []; + list: TPartialPaymentAgentList[] = []; + agents: TAgent[] = []; container = Constants.containers.payment_agent; error = new ErrorStore(); - filtered_list = []; + filtered_list: TPartialPaymentAgentList[] = []; is_name_selected = true; is_search_loading = false; is_withdraw = false; is_try_withdraw_successful = false; is_withdraw_successful = false; - confirm = {}; - receipt = {}; - selected_bank = 0; - supported_banks = []; + confirm: TPaymentAgentWithdrawConfirm | Record = {}; + receipt: TPaymentAgentWithdrawReceipt = {}; + selected_bank: number | string = 0; + supported_banks: TSupportedBank[] = []; active_tab_index = 0; search_term = ''; has_payment_agent_search_warning = false; + onRemount: VoidFunction | null = null; - setActiveTabIndex(index) { + setActiveTabIndex(index: number) { this.active_tab_index = index; } - setActiveTab(index) { + setActiveTab(index: number) { this.setActiveTabIndex(index); } + setOnRemount(func: VoidFunction): void { + this.onRemount = func; + } + get is_payment_agent_visible() { return !!(this.filtered_list.length || this.agents.length || this.has_payment_agent_search_warning); } @@ -95,12 +116,12 @@ export default class PaymentAgentStore { return this.WS.authorized.paymentAgentList(residence, currency); } - async getPaymentAgentDetails() { + async getPaymentAgentDetails(): Promise { const { paymentagent_details } = await this.WS.authorized.paymentAgentDetails(); return paymentagent_details; } - addSupportedBank(bank) { + addSupportedBank(bank: string) { const supported_bank_exists = this.supported_banks.find( supported_bank => supported_bank.value === bank.toLowerCase() ); @@ -109,13 +130,13 @@ export default class PaymentAgentStore { } } - clearSuppertedBanks() { + clearSupportedBanks() { this.supported_banks = []; } sortSupportedBanks() { // sort supported banks alphabetically by value, the option 'All payment agents' with value 0 should be on top - this.supported_banks.replace( + (this.supported_banks as IObservableArray).replace( this.supported_banks.slice().sort((a, b) => { if (a.value < b.value) { return -1; @@ -128,7 +149,7 @@ export default class PaymentAgentStore { ); } - setList(pa_list) { + setList(pa_list: TPartialPaymentAgentList) { this.list.push(pa_list); } @@ -136,14 +157,12 @@ export default class PaymentAgentStore { this.list = []; } - async setPaymentAgentList(pa_list) { + async setPaymentAgentList(pa_list?: TPaymentAgent[]) { const { setLoading } = this.root_store.modules.cashier.general_store; - const payment_agent_list = pa_list || (await this.getPaymentAgentList()); this.clearList(); - this.clearSuppertedBanks(); - // TODO: Once telephone, url and supported_banks removed from paymentagent_list.list we can remove them and just use the plural ones + this.clearSupportedBanks(); try { - payment_agent_list.paymentagent_list?.list.forEach(payment_agent => { + (pa_list || (await this.getPaymentAgentList()).paymentagent_list?.list)?.forEach(payment_agent => { this.setList({ currency: payment_agent.currencies, deposit_commission: payment_agent.deposit_commission, @@ -153,13 +172,14 @@ export default class PaymentAgentStore { min_withdrawal: payment_agent.min_withdrawal, name: payment_agent.name, paymentagent_loginid: payment_agent.paymentagent_loginid, - phones: payment_agent?.phone_numbers || payment_agent?.telephone, - supported_banks: payment_agent?.supported_payment_methods, - urls: payment_agent?.urls || payment_agent?.url, + phone_numbers: payment_agent.phone_numbers, + supported_banks: payment_agent.supported_payment_methods, + urls: payment_agent?.urls, withdrawal_commission: payment_agent.withdrawal_commission, }); const supported_banks_array = payment_agent?.supported_payment_methods - .map(bank => { + .map((bank: { payment_method?: string }) => { + if (bank.payment_method === undefined) return ''; const payment_method = getNormalizedPaymentMethod( bank.payment_method, Constants.payment_methods @@ -168,7 +188,7 @@ export default class PaymentAgentStore { return ['Neteller', 'Skrill'].includes(payment_method) ? '' : payment_method; }) .filter(Boolean); - supported_banks_array.forEach(bank => this.addSupportedBank(bank)); + supported_banks_array.forEach((bank: string) => this.addSupportedBank(bank)); }); shuffleArray(this.list); } catch (e) { @@ -180,7 +200,7 @@ export default class PaymentAgentStore { this.sortSupportedBanks(); } - filterPaymentAgentList(bank) { + filterPaymentAgentList(bank?: number | string) { this.setPaymentAgentSearchWarning(false); const { common } = this.root_store; @@ -193,11 +213,11 @@ export default class PaymentAgentStore { const bank_index = supported_banks .map(supported_bank => getNormalizedPaymentMethod( - supported_bank.payment_method, + supported_bank.payment_method || '', Constants.payment_methods ).toLowerCase() ) - .indexOf(bank || this.selected_bank); + .indexOf((bank || this.selected_bank).toString()); if (bank_index !== -1) this.filtered_list.push(payment_agent); } @@ -207,8 +227,8 @@ export default class PaymentAgentStore { } if (this.search_term) { this.filtered_list = this.filtered_list.filter(payment_agent => { - return payment_agent.name.toLocaleLowerCase().includes(this.search_term.toLocaleLowerCase()); - }); + return payment_agent.name?.toLocaleLowerCase().includes(this.search_term.toLocaleLowerCase()); + }) as IObservableArray; if (this.filtered_list.length === 0) { this.setPaymentAgentSearchWarning(true); @@ -222,19 +242,19 @@ export default class PaymentAgentStore { } } - setSearchTerm(search_term) { + setSearchTerm(search_term: string) { this.search_term = search_term; } - setIsSearchLoading(value) { + setIsSearchLoading(value: boolean) { this.is_search_loading = value; } - setPaymentAgentSearchWarning(value) { + setPaymentAgentSearchWarning(value: boolean) { this.has_payment_agent_search_warning = value; } - onChangePaymentMethod({ target }) { + onChangePaymentMethod({ target }: TTarget) { const value = target.value === '0' ? parseInt(target.value) : target.value; this.selected_bank = value; this.filterPaymentAgentList(value); @@ -244,22 +264,17 @@ export default class PaymentAgentStore { this.is_withdraw = is_withdraw; } - setIsTryWithdrawSuccessful(is_try_withdraw_successful) { - this.error.setErrorMessage(''); + setIsTryWithdrawSuccessful(is_try_withdraw_successful: boolean) { + this.error.setErrorMessage({ code: '', message: '' }); this.is_try_withdraw_successful = is_try_withdraw_successful; } - setIsWithdrawSuccessful(is_withdraw_successful) { + setIsWithdrawSuccessful(is_withdraw_successful: boolean) { this.is_withdraw_successful = is_withdraw_successful; } - setConfirmation({ amount, currency, loginid, payment_agent_name }) { - this.confirm = { - amount, - currency, - loginid, - payment_agent_name, - }; + setConfirmation(confirm: TPaymentAgentWithdrawConfirm | Record) { + this.confirm = confirm; } setReceipt({ @@ -269,7 +284,7 @@ export default class PaymentAgentStore { payment_agent_name, payment_agent_phone, payment_agent_url, - }) { + }: TPaymentAgentWithdrawReceipt) { this.receipt = { amount_transferred, payment_agent_email, @@ -280,14 +295,14 @@ export default class PaymentAgentStore { }; } - addPaymentAgent(payment_agent) { + addPaymentAgent(payment_agent: DeepPartial) { this.agents.push({ text: payment_agent.name, value: payment_agent.paymentagent_loginid, max_withdrawal: payment_agent.max_withdrawal, min_withdrawal: payment_agent.min_withdrawal, email: payment_agent.email, - phone: payment_agent.phone_numbers, + phone_numbers: payment_agent.phone_numbers, url: payment_agent.urls, }); } @@ -297,8 +312,8 @@ export default class PaymentAgentStore { const { setLoading, onMountCommon } = modules.cashier.general_store; setLoading(true); - this.onRemount = this.onMountPaymentAgentWithdraw; - onMountCommon(); + this.setOnRemount(() => this.onMountPaymentAgentWithdraw); + await onMountCommon(); this.setIsWithdraw(true); this.setIsWithdrawSuccessful(false); @@ -306,11 +321,11 @@ export default class PaymentAgentStore { if (!this.agents.length) { const payment_agent_list = await this.getPaymentAgentList(); - payment_agent_list.paymentagent_list.list.forEach(payment_agent => { + payment_agent_list.paymentagent_list?.list.forEach(payment_agent => { this.addPaymentAgent(payment_agent); }); if ( - !payment_agent_list.paymentagent_list.list.length && + !payment_agent_list.paymentagent_list?.list.length && window.location.pathname.endsWith(routes.cashier_pa) ) { common.routeTo(routes.cashier_deposit); @@ -319,8 +334,13 @@ export default class PaymentAgentStore { setLoading(false); } - async requestTryPaymentAgentWithdraw({ loginid, currency, amount, verification_code }) { - this.error.setErrorMessage(''); + async requestTryPaymentAgentWithdraw({ + loginid, + currency, + amount, + verification_code, + }: TPaymentAgentWithdrawRequest) { + this.error.setErrorMessage({ code: '', message: '' }); const payment_agent_withdraw = await this.WS.authorized.paymentAgentWithdraw({ loginid, currency, @@ -328,27 +348,27 @@ export default class PaymentAgentStore { verification_code, dry_run: 1, }); - if (+payment_agent_withdraw.paymentagent_withdraw === 2) { + if (Number(payment_agent_withdraw.paymentagent_withdraw) === 2) { const selected_agent = this.agents.find(agent => agent.value === loginid); this.setConfirmation({ amount, currency, loginid, - payment_agent_name: selected_agent?.text || payment_agent_withdraw.paymentagent_name, + payment_agent_name: selected_agent?.text || payment_agent_withdraw.paymentagent_name || '', }); this.setIsTryWithdrawSuccessful(true); } else { - this.error.setErrorMessage(payment_agent_withdraw.error, this.resetPaymentAgent); + this.error.setErrorMessage(payment_agent_withdraw.error as TServerError, this.resetPaymentAgent); } } resetPaymentAgent = () => { const { client, modules } = this.root_store; const { active_container } = modules.cashier.general_store; - const container = Constants.map_action[active_container]; + const container = Constants.map_action[active_container as 'withdraw' | 'payment_agent']; client.setVerificationCode('', container); - this.error.setErrorMessage(''); + this.error.setErrorMessage({ code: '', message: '' }); this.setIsWithdraw(false); this.setIsWithdrawSuccessful(false); this.setIsTryWithdrawSuccessful(false); @@ -359,22 +379,22 @@ export default class PaymentAgentStore { const { setLoading, onMountCommon } = this.root_store.modules.cashier.general_store; setLoading(true); - this.onRemount = this.onMountPaymentAgentList; + this.onRemount = () => this.onMountPaymentAgentList; await onMountCommon(); await this.getPaymentAgentList(); setLoading(false); } - async requestPaymentAgentWithdraw({ loginid, currency, amount, verification_code }) { - this.error.setErrorMessage(''); + async requestPaymentAgentWithdraw({ loginid, currency, amount, verification_code }: TPaymentAgentWithdrawRequest) { + this.error.setErrorMessage({ code: '', message: '' }); const payment_agent_withdraw = await this.WS.authorized.paymentAgentWithdraw({ loginid, currency, amount, verification_code, }); - if (+payment_agent_withdraw.paymentagent_withdraw === 1) { + if (Number(payment_agent_withdraw.paymentagent_withdraw) === 1) { const selected_agent = this.agents.find(agent => agent.value === loginid); this.setReceipt({ amount_transferred: formatMoney(currency, amount, true), @@ -382,7 +402,7 @@ export default class PaymentAgentStore { payment_agent_email: selected_agent.email, payment_agent_id: selected_agent.value, payment_agent_name: selected_agent.text, - payment_agent_phone: selected_agent.phone, + payment_agent_phone: selected_agent.phone_numbers, payment_agent_url: selected_agent.url, }), }); @@ -390,7 +410,7 @@ export default class PaymentAgentStore { this.setIsTryWithdrawSuccessful(false); this.setConfirmation({}); } else { - this.error.setErrorMessage(payment_agent_withdraw.error, this.resetPaymentAgent); + this.error.setErrorMessage(payment_agent_withdraw.error as TServerError, this.resetPaymentAgent); } } } diff --git a/packages/cashier/src/stores/payment-agent-transfer-store.js b/packages/cashier/src/stores/payment-agent-transfer-store.ts similarity index 70% rename from packages/cashier/src/stores/payment-agent-transfer-store.js rename to packages/cashier/src/stores/payment-agent-transfer-store.ts index 60d0877d2268..421d4ff4f951 100644 --- a/packages/cashier/src/stores/payment-agent-transfer-store.js +++ b/packages/cashier/src/stores/payment-agent-transfer-store.ts @@ -1,9 +1,18 @@ import { action, observable, makeObservable } from 'mobx'; import Constants from 'Constants/constants'; import ErrorStore from './error-store'; +import { PaymentAgentListResponse } from '@deriv/api-types'; +import { + TPaymentAgentTransferRequest, + TPaymentAgentTransferReceipt, + TPaymentAgentTransferConfirm, + TTransferLimit, + TWebSocket, + TRootStore, +} from '../types'; export default class PaymentAgentTransferStore { - constructor({ WS, root_store }) { + constructor(public WS: TWebSocket, public root_store: TRootStore) { makeObservable(this, { container: observable, error: observable, @@ -28,23 +37,24 @@ export default class PaymentAgentTransferStore { } container = Constants.containers.payment_agent_transfer; - error = new ErrorStore(); + error: TRootStore['modules']['cashier']['error'] = new ErrorStore(); is_try_transfer_successful = false; is_transfer_successful = false; - confirm = {}; - receipt = {}; - transfer_limit = {}; + confirm: TPaymentAgentTransferConfirm = {}; + receipt: TPaymentAgentTransferReceipt = {}; + transfer_limit: TTransferLimit = {}; + onRemount: VoidFunction | null = null; - setIsTryTransferSuccessful(is_try_transfer_successful) { - this.error.setErrorMessage(''); + setIsTryTransferSuccessful(is_try_transfer_successful: boolean) { + this.error.setErrorMessage({ code: '', message: '' }); this.is_try_transfer_successful = is_try_transfer_successful; } - setIsTransferSuccessful(is_transfer_successful) { + setIsTransferSuccessful(is_transfer_successful: boolean) { this.is_transfer_successful = is_transfer_successful; } - setConfirmationPaymentAgentTransfer({ amount, client_id, client_name, description }) { + setConfirmationPaymentAgentTransfer({ amount, client_id, client_name, description }: TPaymentAgentTransferConfirm) { this.confirm = { amount, client_id, @@ -53,7 +63,7 @@ export default class PaymentAgentTransferStore { }; } - setReceiptPaymentAgentTransfer({ amount_transferred, client_id, client_name }) { + setReceiptPaymentAgentTransfer({ amount_transferred, client_id, client_name }: TPaymentAgentTransferReceipt) { this.receipt = { amount_transferred, client_id, @@ -61,9 +71,9 @@ export default class PaymentAgentTransferStore { }; } - async getCurrentPaymentAgent(response_payment_agent) { + async getCurrentPaymentAgent(response_payment_agent: PaymentAgentListResponse) { const { client, modules } = this.root_store; - const payment_agent_listed = response_payment_agent.paymentagent_list.list.find( + const payment_agent_listed = response_payment_agent.paymentagent_list?.list.find( agent => agent.paymentagent_loginid === client.loginid ); const current_payment_agent = @@ -71,10 +81,10 @@ export default class PaymentAgentTransferStore { return current_payment_agent ?? {}; } - setMinMaxPaymentAgentTransfer({ min_withdrawal, max_withdrawal }) { + setMinMaxPaymentAgentTransfer({ min_withdrawal, max_withdrawal }: TTransferLimit) { this.transfer_limit = { - min: min_withdrawal, - max: max_withdrawal, + min_withdrawal, + max_withdrawal, }; } @@ -82,7 +92,7 @@ export default class PaymentAgentTransferStore { const { general_store, payment_agent } = this.root_store.modules.cashier; general_store.setLoading(true); - this.onRemount = this.onMountPaymentAgentTransfer; + this.onRemount = () => this.onMountPaymentAgentTransfer; await general_store.onMountCommon(); if (!this.transfer_limit.min_withdrawal) { const response = await payment_agent.getPaymentAgentList(); @@ -92,8 +102,13 @@ export default class PaymentAgentTransferStore { general_store.setLoading(false); } - requestTryPaymentAgentTransfer = async ({ amount, currency, description, transfer_to }) => { - this.error.setErrorMessage(''); + requestTryPaymentAgentTransfer = async ({ + amount, + currency, + description, + transfer_to, + }: TPaymentAgentTransferRequest) => { + this.error.setErrorMessage({ code: '', message: '' }); const payment_agent_transfer = await this.WS.authorized.paymentAgentTransfer({ amount, currency, @@ -101,7 +116,7 @@ export default class PaymentAgentTransferStore { transfer_to, dry_run: 1, }); - if (+payment_agent_transfer.paymentagent_transfer === 2) { + if (Number(payment_agent_transfer.paymentagent_transfer) === 2) { // show confirmation screen this.setConfirmationPaymentAgentTransfer({ client_id: transfer_to, @@ -117,15 +132,20 @@ export default class PaymentAgentTransferStore { return payment_agent_transfer; }; - requestPaymentAgentTransfer = async ({ amount, currency, description, transfer_to }) => { - this.error.setErrorMessage(''); + requestPaymentAgentTransfer = async ({ + amount, + currency, + description, + transfer_to, + }: TPaymentAgentTransferRequest) => { + this.error.setErrorMessage({ code: '', message: '' }); const payment_agent_transfer = await this.WS.authorized.paymentAgentTransfer({ amount, currency, description, transfer_to, }); - if (+payment_agent_transfer.paymentagent_transfer === 1) { + if (Number(payment_agent_transfer.paymentagent_transfer) === 1) { this.setReceiptPaymentAgentTransfer({ amount_transferred: amount, client_id: transfer_to, @@ -143,6 +163,6 @@ export default class PaymentAgentTransferStore { resetPaymentAgentTransfer = () => { this.setIsTransferSuccessful(false); - this.error.setErrorMessage(''); + this.error.setErrorMessage({ code: '', message: '' }); }; } diff --git a/packages/cashier/src/stores/transaction-history-store.ts b/packages/cashier/src/stores/transaction-history-store.ts index 0538926957b9..ee4458e3c0f4 100644 --- a/packages/cashier/src/stores/transaction-history-store.ts +++ b/packages/cashier/src/stores/transaction-history-store.ts @@ -40,7 +40,7 @@ export default class TransactionHistoryStore { is_loading = false; selected_crypto_transaction_id = ''; selected_crypto_status = ''; - selected_crypto_status_description = ''; + selected_crypto_status_description: JSX.Element | string = ''; async onMount() { const { currency, switched } = this.root_store.client; @@ -49,7 +49,7 @@ export default class TransactionHistoryStore { if (is_crypto && !switched) { this.setLoading(true); await this.unsubscribeCryptoTransactions(); - this.getCryptoTransactions(); + await this.getCryptoTransactions(); this.setLoading(false); } } @@ -63,8 +63,8 @@ export default class TransactionHistoryStore { }); } - getCryptoTransactions(): void { - this.WS.subscribeCashierPayments?.(response => { + async getCryptoTransactions() { + await this.WS.subscribeCashierPayments?.(response => { if (!response.error) { const { crypto } = response.cashier_payments; this.updateCryptoTransactions(crypto); @@ -126,7 +126,7 @@ export default class TransactionHistoryStore { this.selected_crypto_status = status; } - setSelectedCryptoStatusDescription(description: string): void { + setSelectedCryptoStatusDescription(description: JSX.Element | string): void { this.selected_crypto_status_description = description; } @@ -134,7 +134,7 @@ export default class TransactionHistoryStore { this.is_crypto_transactions_status_modal_visible = is_visible; } - showCryptoTransactionsStatusModal(description: string, name: string): void { + showCryptoTransactionsStatusModal(description: JSX.Element | string, name: string): void { this.setSelectedCryptoStatusDescription(description); this.setSelectedCryptoStatus(name); this.setIsCryptoTransactionsStatusModalVisible(true); diff --git a/packages/cashier/src/stores/withdraw-store.ts b/packages/cashier/src/stores/withdraw-store.ts index fb18d2fcf770..9ec944c6b8e9 100644 --- a/packages/cashier/src/stores/withdraw-store.ts +++ b/packages/cashier/src/stores/withdraw-store.ts @@ -5,7 +5,7 @@ import { localize } from '@deriv/translations'; import ReadMoreWrapper from 'Components/read-more-wrapper'; import Constants from 'Constants/constants'; import ErrorStore from './error-store'; -import { TRootStore, TWebSocket } from 'Types'; +import { TWebSocket, TRootStore } from '../types'; export default class WithdrawStore { constructor(public WS: TWebSocket, public root_store: TRootStore) { diff --git a/packages/cashier/src/types/crypto-transaction-details.types.ts b/packages/cashier/src/types/crypto-transaction-details.types.ts index 8a5644a80427..f05fbd556dfa 100644 --- a/packages/cashier/src/types/crypto-transaction-details.types.ts +++ b/packages/cashier/src/types/crypto-transaction-details.types.ts @@ -1,13 +1,15 @@ +import { TTransactionType, TStatusCode } from './transactions.types'; + export type TCryptoTransactionDetails = { address_hash: string; address_url: string; amount: number; id: string; is_valid_to_cancel: number; - status_code: string; + status_code: TStatusCode; status_message: string; submit_date: number; - transaction_type: string; + transaction_type: TTransactionType; transaction_hash: string; transaction_url: string; }; diff --git a/packages/cashier/src/types/index.ts b/packages/cashier/src/types/index.ts index bce8c1be312b..1d90c3686d4b 100644 --- a/packages/cashier/src/types/index.ts +++ b/packages/cashier/src/types/index.ts @@ -1,6 +1,7 @@ export * from './account.types'; export * from './crypto-transaction-details.types'; export * from './error.types'; +export * from './payment-agent.types'; export * from './props.types'; export * from './provider.types'; export * from './root-store.types'; diff --git a/packages/cashier/src/types/payment-agent.types.ts b/packages/cashier/src/types/payment-agent.types.ts new file mode 100644 index 000000000000..5a24b854f0a0 --- /dev/null +++ b/packages/cashier/src/types/payment-agent.types.ts @@ -0,0 +1,67 @@ +import { PaymentagentList, PaymentAgentWithdrawRequest } from '@deriv/api-types'; + +export type TAgent = DeepPartial< + Pick +> & { + text?: string; + url?: { url?: string }[]; + value?: string; +}; + +export type TPaymentAgent = PaymentagentList['list'][0] & { + supported_banks?: { payment_method: string }[]; + currency?: string; + value?: string; +}; + +export type TSupportedBank = { + text: string; + value: string; +}; + +export type TPaymentAgentWithdrawConfirm = Pick & { + loginid: string; + payment_agent_name: string; +}; + +export type TPaymentAgentWithdrawReceipt = { + amount_transferred?: string; + payment_agent_email?: string; + payment_agent_id?: string; + payment_agent_name?: string; + payment_agent_phone?: { phone_number?: string }[]; + payment_agent_url?: { url?: string }[]; +}; + +export type TPartialPaymentAgentList = { + currency?: string; + deposit_commission?: string; + email?: string; + further_information?: string; + max_withdrawal?: string | null; + min_withdrawal?: string | null; + name?: string; + paymentagent_loginid?: string; + phone_numbers?: { phone_number?: string }[]; + supported_banks?: { payment_method?: string }[]; + urls?: { url?: string }[]; + withdrawal_commission?: string; +}; + +export type TPaymentAgentTransferConfirm = { + amount?: number; + client_id?: string; + client_name?: string; + description?: string; +}; + +export type TPaymentAgentTransferReceipt = { + amount_transferred?: number; + client_id?: string; + client_name?: string; +}; + +export type TTransferLimit = { + min_withdrawal?: null | string | number; + max_withdrawal?: null | string | number; +}; diff --git a/packages/cashier/src/types/props.types.ts b/packages/cashier/src/types/props.types.ts index a0aa2457441b..fdc7d562a1b3 100644 --- a/packages/cashier/src/types/props.types.ts +++ b/packages/cashier/src/types/props.types.ts @@ -1,7 +1,5 @@ export type TReactChangeEvent = React.ChangeEvent; -export type TReactChildren = React.ReactNode; - export type TReactMouseEvent = React.MouseEvent; export type TReactFormEvent = React.FormEvent; @@ -9,3 +7,10 @@ export type TReactFormEvent = React.FormEvent; export type TReactElement = React.ReactElement; export type TSideNotesProps = Array | null; + +export type TTarget = { + target: { + name?: string; + value: string; + }; +}; diff --git a/packages/cashier/src/types/root-store.types.ts b/packages/cashier/src/types/root-store.types.ts index 35f46cda1edc..e5f02629f7ad 100644 --- a/packages/cashier/src/types/root-store.types.ts +++ b/packages/cashier/src/types/root-store.types.ts @@ -1,4 +1,4 @@ -import type { TStores } from '@deriv/stores'; +import type { TStores } from '@deriv/stores/types'; import type CashierStore from '../stores/cashier-store'; /** diff --git a/packages/cashier/src/types/transactions.types.ts b/packages/cashier/src/types/transactions.types.ts index 2c8011a5bfb2..657d7b5d1501 100644 --- a/packages/cashier/src/types/transactions.types.ts +++ b/packages/cashier/src/types/transactions.types.ts @@ -1,12 +1,26 @@ +export type TTransactionType = 'deposit' | 'withdrawal'; + +export type TStatusCode = + | 'CONFIRMED' + | 'ERROR' + | 'PENDING' + | 'CANCELLED' + | 'LOCKED' + | 'PERFORMING_BLOCKCHAIN_TXN' + | 'PROCESSING' + | 'REJECTED' + | 'SENT' + | 'VERIFIED'; + export type TTransactionItem = { address_hash: string; address_url: string; amount: number; id: string; is_valid_to_cancel: number; - status_code: string; + status_code: TStatusCode; status_message: string; - submit_date: string; + submit_date: number; + transaction_type: TTransactionType; transaction_hash: string; - transaction_type: string; }; diff --git a/packages/cashier/src/types/websocket.types.ts b/packages/cashier/src/types/websocket.types.ts index bd175a8702d4..1802d3144c72 100644 --- a/packages/cashier/src/types/websocket.types.ts +++ b/packages/cashier/src/types/websocket.types.ts @@ -4,12 +4,19 @@ import { CashierInformationRequest, CashierInformationResponse, CryptoConfig, + GetAccountSettingsResponse, DetailsOfEachMT5Loginid, P2PAdvertInfo, + PaymentAgentTransferRequest, + PaymentAgentTransferResponse, TransferBetweenAccountsResponse, + PaymentAgentWithdrawRequest, + PaymentAgentWithdrawResponse, + PaymentAgentDetailsResponse, + PaymentAgentListResponse, } from '@deriv/api-types'; -import type { TSocketEndpointNames, TSocketResponse } from '@deriv/api/types'; -import type { TTransactionItem } from 'Types'; +import { TSocketEndpointNames, TSocketResponse } from '@deriv/api/types'; +import type { TTransactionItem } from './transactions.types'; export type TCashierPayments = { provider?: string; @@ -33,6 +40,14 @@ export type TServerError = { fields?: string[]; }; +type TPassthrough = { + [k: string]: unknown; +}; + +type TStorage = { + getSettings: () => Promise; +}; + type TServiceTokenRequest = { service_token: number; service: string; @@ -48,6 +63,16 @@ type TServiceTokenResponse = { }; }; +export type TPaymentAgentTransferRequest = Omit< + PaymentAgentTransferRequest, + 'paymentagent_transfer' | 'passthrough' | 'req_id' +>; + +export type TPaymentAgentWithdrawRequest = Omit< + PaymentAgentWithdrawRequest, + 'paymentagent_withdraw' | 'passthrough' | 'req_id' | 'paymentagent_loginid' +> & { loginid: string }; + type TWebSocketCall = { cashier: ( action: string, @@ -55,38 +80,46 @@ type TWebSocketCall = { ) => Promise; cashierPayments?: (request?: TCashierPayments) => Promise; getAccountStatus: () => Promise; + paymentAgentTransfer: ( + request: TPaymentAgentTransferRequest + ) => Promise; p2pAdvertiserInfo?: () => Promise; send?: (obj: unknown) => Promise; + storage: TStorage; transferBetweenAccounts: ( account_from?: string, account_to?: string, currency?: string, amount?: number ) => Promise; + paymentAgentDetails: (passthrough?: TPassthrough, req_id?: number) => Promise; + paymentAgentList: (residence: string, currency: string) => Promise; + paymentAgentWithdraw: (request: TPaymentAgentWithdrawRequest) => Promise; }; export type TWebSocket = { + allPaymentAgentList: (residence: string) => Promise; authorized: TWebSocketCall; balanceAll: () => Promise; cancelCryptoTransaction?: (transaction_id: string) => Promise<{ error: TServerError }>; - cryptoConfig: () => { crypto_config: CryptoConfig }; + cryptoConfig: () => Promise<{ crypto_config: CryptoConfig }>; cryptoWithdraw: ( args: Omit ) => Promise; - mt5LoginList: () => { + mt5LoginList: () => Promise<{ mt5_login_list: DetailsOfEachMT5Loginid[]; - }; + }>; send: (obj: unknown) => Promise<{ error: TServerError; exchange_rates: { rates: { [k: string]: string } } }>; serviceToken: (req: TServiceTokenRequest) => Promise; - subscribeCashierPayments?: (callback: (response: TSubscribeCashierPayments) => void) => void; + subscribeCashierPayments?: (callback: (response: TSubscribeCashierPayments) => void) => Promise; verifyEmail?: (email: string, withdrawal_type: string) => Promise; storage: { - mt5LoginList: () => { + mt5LoginList: () => Promise<{ mt5_login_list: DetailsOfEachMT5Loginid[]; - }; + }>; }; - tradingPlatformAccountsList: (platform: string) => { + tradingPlatformAccountsList: (platform: string) => Promise<{ trading_platform_accounts: (DetailsOfEachMT5Loginid & { account_id: string })[]; - }; + }>; wait: (value: T) => Promise>; }; diff --git a/packages/cashier/src/utils/server_time.js b/packages/cashier/src/utils/server_time.ts similarity index 74% rename from packages/cashier/src/utils/server_time.js rename to packages/cashier/src/utils/server_time.ts index a0777a925f82..a8ccb0c06118 100644 --- a/packages/cashier/src/utils/server_time.js +++ b/packages/cashier/src/utils/server_time.ts @@ -4,16 +4,22 @@ import { PromiseClass } from './utility'; let clock_started = false; const pending = new PromiseClass(); -let server_time, performance_request_time, get_time_interval, update_time_interval, onTimeUpdated; +let server_time: moment.Moment, + performance_request_time: number, + get_time_interval: ReturnType, + update_time_interval: ReturnType, + onTimeUpdated: VoidFunction; const requestTime = () => { performance_request_time = performance.now(); WS.send({ time: 1 }).then(timeCounter); }; -export const init = fncTimeUpdated => { +export const init = (fncTimeUpdated?: VoidFunction) => { if (!clock_started) { - onTimeUpdated = fncTimeUpdated; + if (fncTimeUpdated) { + onTimeUpdated = fncTimeUpdated; + } requestTime(); clearInterval(get_time_interval); get_time_interval = setInterval(requestTime, 30000); @@ -21,7 +27,7 @@ export const init = fncTimeUpdated => { } }; -export const timeCounter = response => { +export const timeCounter = (response: { error: unknown; time: number }) => { if (response.error) return; if (!clock_started) { @@ -45,7 +51,8 @@ export const timeCounter = response => { } }; updateTime(); - pending.resolve(); + pending.resolve?.(); + update_time_interval = setInterval(updateTime, 1000); }; diff --git a/packages/cashier/src/utils/utility.js b/packages/cashier/src/utils/utility.js deleted file mode 100644 index 5664f6cefda2..000000000000 --- a/packages/cashier/src/utils/utility.js +++ /dev/null @@ -1,117 +0,0 @@ -import { getCurrencyDisplayCode } from '@deriv/shared'; - -const template = (string, content) => { - let to_replace = content; - if (content && !Array.isArray(content)) { - to_replace = [content]; - } - return string.replace(/\[_(\d+)]/g, (s, index) => to_replace[+index - 1]); -}; - -/** - * Creates a DOM element and adds any attributes to it. - * - * @param {String} tag_name: the tag to create, e.g. 'div', 'a', etc - * @param {Object} attributes: all the attributes to assign, e.g. { id: '...', class: '...', html: '...', ... } - * @return the created DOM element - */ -const createElement = (tag_name, attributes = {}) => { - const el = document.createElement(tag_name); - Object.keys(attributes).forEach(attr => { - const value = attributes[attr]; - if (attr === 'text') { - el.textContent = value; - } else if (attr === 'html') { - el.html(value); - } else { - el.setAttribute(attr, value); - } - }); - return el; -}; - -let static_hash; -const getStaticHash = () => { - static_hash = - static_hash || (document.querySelector('script[src*="main"]').getAttribute('src') || '').split('.')[1]; - return static_hash; -}; - -class PromiseClass { - constructor() { - this.promise = new Promise((resolve, reject) => { - this.reject = reject; - this.resolve = resolve; - }); - } -} - -// TODO: [duplicate_code] - Move this to shared package -// eu countries to support -const eu_countries = [ - 'it', - 'de', - 'fr', - 'lu', - 'gr', - 'mf', - 'es', - 'sk', - 'lt', - 'nl', - 'at', - 'bg', - 'si', - 'cy', - 'be', - 'ro', - 'hr', - 'pt', - 'pl', - 'lv', - 'ee', - 'cz', - 'fi', - 'hu', - 'dk', - 'se', - 'ie', - 'im', - 'gb', - 'mt', -]; -// TODO: [duplicate_code] - Move this to shared package -// check if client is from EU -const isEuCountry = country => eu_countries.includes(country); - -// check if mlt or dxtrade for account text -const getAccountText = account => { - let account_text = ''; - if (account.is_dxtrade || account.is_mt) { - account_text = account.text; - } else { - account_text = getCurrencyDisplayCode(account.text); - } - - return account_text; -}; - -const getNormalizedPaymentMethod = (payment_method, constants, is_for_icon = false) => { - const method = is_for_icon ? payment_method.replace(/[' ',-]/g, '').toLowerCase() : payment_method; - - const normalized_payment_method = Object.entries(constants).reduce( - (pay_method, [key, value]) => (value.some(el => el === method) ? key : pay_method), - '' - ); - return is_for_icon ? normalized_payment_method : normalized_payment_method || payment_method; -}; - -export { - createElement, - getAccountText, - getNormalizedPaymentMethod, - getStaticHash, - isEuCountry, - PromiseClass, - template, -}; diff --git a/packages/cashier/src/utils/utility.ts b/packages/cashier/src/utils/utility.ts new file mode 100644 index 000000000000..a57fa12d4579 --- /dev/null +++ b/packages/cashier/src/utils/utility.ts @@ -0,0 +1,43 @@ +import { getCurrencyDisplayCode } from '@deriv/shared'; +import Constants from 'Constants/constants'; + +class PromiseClass { + promise: Promise; + reject?: (reason?: unknown) => void; + resolve?: (value?: unknown) => void; + + constructor() { + this.promise = new Promise((resolve, reject) => { + this.reject = reject; + this.resolve = resolve; + }); + } +} + +// check if mlt or dxtrade for account text +const getAccountText = (account: { is_dxtrade: boolean; is_mt: boolean; text: string }) => { + let account_text: string; + if (account.is_dxtrade || account.is_mt) { + account_text = account.text; + } else { + account_text = getCurrencyDisplayCode(account.text); + } + + return account_text; +}; + +const getNormalizedPaymentMethod = ( + payment_method: string, + constants: typeof Constants.icon_payment_methods | typeof Constants.payment_methods, + is_for_icon = false +) => { + const method = is_for_icon ? payment_method.replace(/[' ,-]/g, '').toLowerCase() : payment_method; + + const normalized_payment_method = Object.entries(constants).reduce( + (pay_method, [key, value]) => (value.some((el: string) => el === method) ? key : pay_method), + '' + ); + return is_for_icon ? normalized_payment_method : normalized_payment_method || payment_method; +}; + +export { getAccountText, getNormalizedPaymentMethod, PromiseClass }; diff --git a/packages/cashier/src/utils/validator/index.js b/packages/cashier/src/utils/validator/index.js deleted file mode 100644 index 1b7f5cfa8611..000000000000 --- a/packages/cashier/src/utils/validator/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './validator'; diff --git a/packages/cashier/src/utils/validator/validator.js b/packages/cashier/src/utils/validator/validator.js deleted file mode 100644 index 5d6605e5a9e3..000000000000 --- a/packages/cashier/src/utils/validator/validator.js +++ /dev/null @@ -1,112 +0,0 @@ -import { getPreBuildDVRs } from '@deriv/shared'; -import { template } from '../utility'; -import Error from './errors'; - -class Validator { - constructor(input, rules, store) { - this.input = input; - this.rules = rules; - this.store = store || null; - this.errors = new Error(); - - this.error_count = 0; - } - - /** - * Add failure and error message for given rule - * - * @param {string} attribute - * @param {object} rule - */ - addFailure(attribute, rule, error_message) { - let message = error_message || rule.options.message || getPreBuildDVRs()[rule.name].message(); - if (rule.name === 'length') { - message = template(message, [ - rule.options.min === rule.options.max ? rule.options.min : `${rule.options.min}-${rule.options.max}`, - ]); - } else if (rule.name === 'min') { - message = template(message, [rule.options.min]); - } else if (rule.name === 'not_equal') { - message = template(message, [rule.options.name1, rule.options.name2]); - } - this.errors.add(attribute, message); - this.error_count++; - } - - /** - * Runs validator - * - * @return {boolean} Whether it passes; true = passes, false = fails - */ - check() { - Object.keys(this.input).forEach(attribute => { - if (!Object.prototype.hasOwnProperty.call(this.rules, attribute)) { - return; - } - - this.rules[attribute].forEach(rule => { - const ruleObject = Validator.getRuleObject(rule); - - if (!ruleObject.validator && typeof ruleObject.validator !== 'function') { - return; - } - - if (ruleObject.options.condition && !ruleObject.options.condition(this.store)) { - return; - } - - if (this.input[attribute] === '' && ruleObject.name !== 'req') { - return; - } - - let is_valid, error_message; - if (ruleObject.name === 'number') { - const { is_ok, message } = ruleObject.validator( - this.input[attribute], - ruleObject.options, - this.store, - this.input - ); - is_valid = is_ok; - error_message = message; - } else { - is_valid = ruleObject.validator(this.input[attribute], ruleObject.options, this.store, this.input); - } - - if (!is_valid) { - this.addFailure(attribute, ruleObject, error_message); - } - }); - }); - return !this.error_count; - } - - /** - * Determine if validation passes - * - * @return {boolean} - */ - isPassed() { - return this.check(); - } - - /** - * Converts the rule array to an object - * - * @param {array} rule - * @return {object} - */ - static getRuleObject(rule) { - const is_rule_string = typeof rule === 'string'; - const rule_object = { - name: is_rule_string ? rule : rule[0], - options: is_rule_string ? {} : rule[1] || {}, - }; - - rule_object.validator = rule_object.name === 'custom' ? rule[1].func : getPreBuildDVRs()[rule_object.name].func; - - return rule_object; - } -} - -export default Validator; diff --git a/packages/components/src/components/expansion-panel/expansion-panel.tsx b/packages/components/src/components/expansion-panel/expansion-panel.tsx index 5c3d5b83844a..9709e0f6337b 100644 --- a/packages/components/src/components/expansion-panel/expansion-panel.tsx +++ b/packages/components/src/components/expansion-panel/expansion-panel.tsx @@ -5,7 +5,7 @@ import Icon from '../icon'; import { TItem } from '../types/common.types'; type TExpansionPanel = { - message: { header: React.ReactNode; content: Array & Array }; + message: { header: React.ReactNode; content: (Array & Array) | React.ReactNode }; onResize?: () => void; }; diff --git a/packages/components/src/components/input/input.tsx b/packages/components/src/components/input/input.tsx index 92f79cbbb3e1..078efae5fbc9 100644 --- a/packages/components/src/components/input/input.tsx +++ b/packages/components/src/components/input/input.tsx @@ -28,11 +28,13 @@ export type TInputProps = { name?: string; onBlur?: (e: React.FocusEvent) => void; onChange?: (e: React.ChangeEvent) => void; + onKeyUp?: React.FormEventHandler; + onKeyDown?: React.FormEventHandler; onFocus?: (e: React.FocusEvent) => void; onPaste?: (e: React.ClipboardEvent) => void; placeholder?: string; required?: boolean; - trailing_icon?: React.ReactElement; + trailing_icon?: React.ReactElement | null; type: string; value?: string; warn?: string; diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index e50443122508..4a1292d3915d 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -25,6 +25,7 @@ export * from './utils/storage'; export * from './utils/string'; export * from './utils/url'; export * from './utils/validation'; +export * from './utils/validator'; export * from './services'; export * from './utils/helpers'; export * from './utils/constants'; diff --git a/packages/shared/src/utils/validator/__tests__/error.spec.tsx b/packages/shared/src/utils/validator/__tests__/error.spec.tsx new file mode 100644 index 000000000000..ffe7d027f625 --- /dev/null +++ b/packages/shared/src/utils/validator/__tests__/error.spec.tsx @@ -0,0 +1,44 @@ +import Errors from '../errors'; + +describe('Errors', () => { + let errors: Errors; + + beforeEach(() => { + errors = new Errors(); + errors.add('Error', '100'); + }); + + it('should add a new error to the errors', () => { + errors.add('Error', '101'); + expect(errors.errors.Error).toHaveLength(2); + }); + + it('should not add an error if already existed', () => { + errors.add('Error', '100'); + expect(errors.errors.Error).toHaveLength(1); + }); + + it('should return all errors', () => { + expect(errors.all()).toEqual({ Error: ['100'] }); + }); + + it('should return first error if attribute exists', () => { + expect(errors.first('Error')).toEqual('100'); + }); + + it('should return data if attribute exists', () => { + expect(errors.get('Error')).toEqual(['100']); + }); + + it('should return an empty array if attribute does not exist', () => { + expect(errors.get('')).toEqual([]); + }); + + it('should return "true" if attribute exists', () => { + expect(errors.has('Error')).toBeTruthy(); + }); + + it('should return "false" if the attribute does not exist', () => { + expect(errors.has('')).toBeFalsy(); + }); +}); diff --git a/packages/cashier/src/utils/validator/errors.js b/packages/shared/src/utils/validator/errors.ts similarity index 79% rename from packages/cashier/src/utils/validator/errors.js rename to packages/shared/src/utils/validator/errors.ts index 8c3307765b64..23bd74efad9e 100644 --- a/packages/cashier/src/utils/validator/errors.js +++ b/packages/shared/src/utils/validator/errors.ts @@ -1,9 +1,11 @@ class Errors { + errors: { [key: string]: string[] }; + constructor() { this.errors = {}; } - add(attribute, message) { + add(attribute: string, message: string) { if (!this.has(attribute)) { this.errors[attribute] = []; } @@ -17,14 +19,14 @@ class Errors { return this.errors; } - first(attribute) { + first(attribute: string) { if (this.has(attribute)) { return this.errors[attribute][0]; } return null; } - get(attribute) { + get(attribute: string) { if (this.has(attribute)) { return this.errors[attribute]; } @@ -32,7 +34,7 @@ class Errors { return []; } - has(attribute) { + has(attribute: string) { return Object.prototype.hasOwnProperty.call(this.errors, attribute); } } diff --git a/packages/shared/src/utils/validator/index.ts b/packages/shared/src/utils/validator/index.ts new file mode 100644 index 000000000000..16f9da555ed5 --- /dev/null +++ b/packages/shared/src/utils/validator/index.ts @@ -0,0 +1,3 @@ +import Validator from './validator'; + +export { Validator }; diff --git a/packages/shared/src/utils/validator/validator.ts b/packages/shared/src/utils/validator/validator.ts new file mode 100644 index 000000000000..2e008160fd44 --- /dev/null +++ b/packages/shared/src/utils/validator/validator.ts @@ -0,0 +1,141 @@ +import { TOptions, getPreBuildDVRs, TInitPreBuildDVRs } from '../validation/declarative-validation-rules'; +import Error from './errors'; + +type TRuleOptions = { + func: (value: string | number, options?: TOptions, store?: unknown, inputs?: unknown) => boolean; + condition: (store: unknown) => boolean; + message: string; +} & TOptions; + +type TRule = string | Array; + +const template = (string: string, content: string | Array) => { + let to_replace = content; + if (content && !Array.isArray(content)) { + to_replace = [content]; + } + return string.replace(/\[_(\d+)]/g, (s, index) => to_replace[+index - 1]); +}; + +class Validator { + input: { [key: string]: any }; + rules: Partial; + store: unknown; + errors: Error; + error_count: number; + + constructor(input: { [key: string]: unknown }, rules: Partial, store: unknown) { + this.input = input; + this.rules = rules; + this.store = store; + this.errors = new Error(); + + this.error_count = 0; + } + + /** + * Add failure and error message for given rule + * + * @param {string} attribute + * @param {object} rule + */ + addFailure(attribute: string, rule: { name: string; options: TRuleOptions }, error_message?: string) { + let message = + error_message || + rule.options.message || + (getPreBuildDVRs() as unknown as { [key: string]: { message: () => string } })[rule.name].message(); + if (rule.name === 'length') { + message = template(message, [ + rule.options.min === rule.options.max + ? rule.options.min!.toString() + : `${rule.options.min}-${rule.options.max}`, + ]); + } else if (rule.name === 'min') { + message = template(message, [rule.options.min!.toString()]); + } + this.errors.add(attribute, message); + this.error_count++; + } + + /** + * Runs validator + * + * @return {boolean} Whether it passes; true = passes, false = fails + */ + check() { + Object.keys(this.input).forEach(attribute => { + if (!Object.prototype.hasOwnProperty.call(this.rules, attribute)) { + return; + } + + (this.rules as unknown as { [key: string]: Array })[attribute].forEach((rule: TRule) => { + const ruleObject = Validator.getRuleObject(rule); + + if (!ruleObject.validator && typeof ruleObject.validator !== 'function') { + return; + } + + if (ruleObject.options.condition && !ruleObject.options.condition(this.store)) { + return; + } + + if (this.input[attribute] === '' && ruleObject.name !== 'req') { + return; + } + + const result = ruleObject.validator(this.input[attribute], ruleObject.options, this.store, this.input); + if (typeof result === 'boolean' && !result) { + this.addFailure(attribute, ruleObject); + } else { + const { is_ok, message } = result as { is_ok: boolean; message: string }; + if (!is_ok) { + this.addFailure(attribute, ruleObject, message); + } + } + }); + }); + return !this.error_count; + } + + /** + * Determine if validation passes + * + * @return {boolean} + */ + isPassed() { + return this.check(); + } + + /** + * Converts the rule array to an object + * + * @param {array} rule + * @return {object} + */ + static getRuleObject(rule: TRule) { + const is_rule_string = typeof rule === 'string'; + const rule_object_name = (is_rule_string ? rule : rule[0]) as string; + const rule_object_options = (is_rule_string ? {} : rule[1] || {}) as TRuleOptions; + return { + name: rule_object_name, + options: rule_object_options, + validator: + rule_object_name === 'custom' + ? rule_object_options.func + : ( + getPreBuildDVRs() as unknown as { + [key: string]: { + func: ( + value: string | number, + options?: TRuleOptions, + store?: unknown, + inputs?: unknown + ) => boolean | { is_ok: boolean; message: string }; + }; + } + )[rule_object_name].func, + }; + } +} + +export default Validator; diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts index 4bf92b04bf4b..15a990bd5d5c 100644 --- a/packages/stores/src/mockStore.ts +++ b/packages/stores/src/mockStore.ts @@ -5,21 +5,22 @@ const mock = (): TStores & { is_mock: boolean } => { return { is_mock: true, client: { + account_settings: {}, accounts: {}, active_account_landing_company: '', account_limits: { daily_transfers: { dxtrade: { - allowed: false, - available: false, + allowed: 0, + available: 0, }, internal: { - allowed: false, - available: false, + allowed: 0, + available: 0, }, mt5: { - allowed: false, - available: false, + allowed: 0, + available: 0, }, }, }, @@ -243,6 +244,7 @@ const mock = (): TStores & { is_mock: boolean } => { sub_section_index: 0, toggleReadyToDepositModal: jest.fn(), is_ready_to_deposit_modal_visible: false, + is_real_acc_signup_on: false, is_need_real_account_for_cashier_modal_visible: false, toggleNeedRealAccountForCashierModal: jest.fn(), setShouldShowCooldownModal: jest.fn(), diff --git a/packages/stores/types.ts b/packages/stores/types.ts index a8b32f4d244a..df2a51edf735 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -1,4 +1,12 @@ -import type { Authorize, DetailsOfEachMT5Loginid, GetAccountStatus, GetLimits, LogOutResponse } from '@deriv/api-types'; +import type { + AccountLimitsResponse, + Authorize, + DetailsOfEachMT5Loginid, + GetAccountStatus, + GetLimits, + GetSettings, + LogOutResponse, +} from '@deriv/api-types'; import type { RouteComponentProps } from 'react-router'; import { ExchangeRatesStore } from './src/stores'; @@ -89,22 +97,17 @@ type TNotification = | ((withdrawal_locked: boolean, deposit_locked: boolean) => TNotificationMessage) | ((excluded_until: number) => TNotificationMessage); -type TAccountStatus = Omit & Partial>; - type TClientStore = { accounts: { [k: string]: TActiveAccount }; active_accounts: TActiveAccount[]; active_account_landing_company: string; - account_limits: { - daily_transfers?: { - [k: string]: { - allowed: boolean; - available: boolean; - }; - }; + account_limits: Partial & { + is_loading?: boolean; + api_initial_load_error?: string; }; account_list: TAccountsList; - account_status: TAccountStatus; + account_settings: GetSettings; + account_status: Omit & Partial>; available_crypto_currencies: string[]; balance?: string | number; can_change_fiat_currency: boolean; @@ -113,7 +116,7 @@ type TClientStore = { currency: string; current_currency_type?: string; current_fiat_currency?: string; - getLimits: () => { get_limits?: GetLimits }; + getLimits: () => Promise<{ get_limits?: GetLimits }>; has_active_real_account: boolean; has_logged_out: boolean; has_maltainvest_account: boolean; @@ -163,7 +166,7 @@ type TClientStore = { setLogout: (status?: boolean) => void; setP2pAdvertiserInfo: () => void; setPreSwitchAccount: (status?: boolean) => void; - switchAccount: (value?: string) => void; + switchAccount: (value?: string) => Promise; switched: boolean; switch_broadcast: boolean; switchEndSignal: () => void; @@ -249,6 +252,7 @@ type TUiStore = { toggleReadyToDepositModal: () => void; toggleSetCurrencyModal: () => void; is_ready_to_deposit_modal_visible: boolean; + is_real_acc_signup_on: boolean; is_need_real_account_for_cashier_modal_visible: boolean; toggleNeedRealAccountForCashierModal: () => void; setShouldShowCooldownModal: (value: boolean) => void; @@ -310,7 +314,7 @@ export type TCoreStores = { ui: TUiStore; // This should be `any` as this property will be handled in each package. // eslint-disable-next-line @typescript-eslint/no-explicit-any - modules: any; + modules: Record; notifications: TNotificationStore; traders_hub: TTradersHubStore; };