diff --git a/.ralphy/progress.txt b/.ralphy/progress.txt new file mode 100644 index 000000000..00c0ca9d7 --- /dev/null +++ b/.ralphy/progress.txt @@ -0,0 +1,9 @@ + +2026-01-21: Setup Test Environment (Prerequisite) +- Installed @testing-library/react, @testing-library/jest-dom, redux-saga-test-plan. +- Configured src/setupTests.js with TextEncoder polyfill and jest-dom. +- Configured package.json for Jest (axios transform). +- Created src/test-utils.js. +- Refactored circular dependencies in reducers/sagas/utils to enable store initialization in tests. +- Fixed existing tests (atomicSwap.test.js, ModalPin.test.js) to match environment and library updates. +- Verified setup with src/__tests__/infrastructure_check.test.js. diff --git a/PRD.md b/PRD.md new file mode 100644 index 000000000..c56f234a8 --- /dev/null +++ b/PRD.md @@ -0,0 +1,24 @@ +# Hathor Wallet Refactoring PRD + +This PRD aggregates the technical debt and bad practices identified in the analysis reports. + +## CRITICAL WORKFLOW +1. **Start with Infrastructure**: Complete the [Test Environment Setup](./prd/infrastructure.md) first. +2. **Fork for Refactoring**: All subsequent category tasks MUST be forked from the stable branch containing the test infrastructure. +3. **Test-Driven**: No refactoring should be committed without corresponding tests. + +## Task Sources +The tasks are organized into specialized files in the `prd/` directory: + +- [ ] [Infrastructure (PREREQUISITE)](./prd/infrastructure.md) +- [ ] [Actions and Store](./prd/actions-store.md) +- [ ] [Components](./prd/components.md) +- [ ] [Modules and General](./prd/modules.md) +- [ ] [Reducers](./prd/reducers.md) +- [ ] [Screens](./prd/screens.md) +- [ ] [Sagas](./prd/sagas.md) +- [ ] [Utils](./prd/utils.md) +- [ ] [Root Files](./prd/root-files.md) +- [ ] [Nano Contract and Reown](./prd/nano-reown.md) +- [ ] [Tokens and Atomic Swap](./prd/tokens-atomic-swap.md) + diff --git a/package-lock.json b/package-lock.json index 31d5fa715..cc15be27b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "@sentry/browser": "7.99.0", "@sentry/cli": "2.27.0", "@testing-library/cypress": "10.0.2", + "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "14.1.2", "@testing-library/user-event": "14.5.1", "buffer": "6.0.3", @@ -65,6 +66,7 @@ "nodemon": "3.0.3", "null-loader": "4.0.1", "process": "0.11.10", + "redux-saga-test-plan": "^4.0.6", "sass": "1.70.0" }, "engines": { @@ -79,6 +81,13 @@ "node": ">=0.10.0" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "license": "MIT", @@ -5517,6 +5526,33 @@ "node": ">=8" } }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, "node_modules/@testing-library/react": { "version": "14.1.2", "dev": true, @@ -10661,6 +10697,13 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/cssdb": { "version": "7.11.2", "funding": [ @@ -14415,6 +14458,13 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fsm-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fsm-iterator/-/fsm-iterator-1.1.0.tgz", + "integrity": "sha512-hg47CNYdIGJ5m9WSKh617LHRdvJo4PiF0VkncFLwPVxKvBEQfSPd1qx/xLV/eSusewEu0C8eUFrsLsWlBgIcOg==", + "dev": true, + "license": "MIT" + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -18595,6 +18645,13 @@ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "license": "MIT" @@ -19004,6 +19061,16 @@ "node": ">=6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.9.0", "license": "MIT", @@ -23588,6 +23655,20 @@ "node": ">=6.0.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/redux": { "version": "4.2.0", "license": "MIT", @@ -23602,6 +23683,23 @@ "@redux-saga/core": "^1.2.1" } }, + "node_modules/redux-saga-test-plan": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/redux-saga-test-plan/-/redux-saga-test-plan-4.0.6.tgz", + "integrity": "sha512-ESdbFoDWCeJ/EiFdUNSCGtA2CC9tnuvHDm6k06gVFa98EIeR2hpzFkGk9kJ1/hpMUnYFp+OOEEITIrZeDYBfFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fsm-iterator": "^1.1.0", + "lodash.isequal": "^4.5.0", + "lodash.ismatch": "^4.4.0" + }, + "peerDependencies": { + "@redux-saga/is": "^1.0.1", + "@redux-saga/symbols": "^1.0.1", + "redux-saga": "^1.0.1" + } + }, "node_modules/redux-thunk": { "version": "2.4.1", "license": "MIT", @@ -25708,6 +25806,19 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "2.0.1", "license": "MIT", diff --git a/package.json b/package.json index c9dcd92e0..f7e0782c2 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,13 @@ "text-summary", "lcov", "clover" - ] + ], + "transformIgnorePatterns": [ + "node_modules/(?!axios|@hathor/wallet-lib)" + ], + "moduleNameMapper": { + "^axios$": "axios/dist/node/axios.cjs" + } }, "productName": "Hathor Wallet", "description": "Light wallet for Hathor Network", @@ -106,6 +112,7 @@ "@sentry/browser": "7.99.0", "@sentry/cli": "2.27.0", "@testing-library/cypress": "10.0.2", + "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "14.1.2", "@testing-library/user-event": "14.5.1", "buffer": "6.0.3", @@ -122,6 +129,7 @@ "nodemon": "3.0.3", "null-loader": "4.0.1", "process": "0.11.10", + "redux-saga-test-plan": "^4.0.6", "sass": "1.70.0" }, "build": { diff --git a/prd/actions-store.md b/prd/actions-store.md new file mode 100644 index 000000000..08c272691 --- /dev/null +++ b/prd/actions-store.md @@ -0,0 +1,16 @@ +# Actions and Store PRD + +**Source Report:** [reports/actions-store-bad-practices.md](../reports/actions-store-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Move functions and promises from Redux state to sagas (actions/index.js: 245, 250, 875-878, 888-891, 901-904, 914-917, 927-930, 940-943) +- [ ] Critical: Re-enable Redux Toolkit serialization check with targeted ignores (store/index.js: 19-23) +- [ ] High: Move hardcoded action type strings to constants object (actions/index.js: 127, 132, 137, 142, 147, 152, 157, 162, 167, 172, 177, 182, 187, 194, 202, 209, 215, 220, 225, 230, 235, 240, 245, 250, 255, 262) +- [ ] High: Make actions FSA-compliant by moving root properties to payload (actions/index.js: 268-272, 278-282, 287-290, 304-308, 355-360, 395-398) +- [ ] Medium: Remove redundant redux-thunk middleware (store/index.js: 9, 15) +- [ ] Medium: Complete migration to types syntax in reducers (reducers/index.js: 377) +- [ ] Low: Modernize state update pattern to use spread instead of Object.assign (reducers/index.js: 318-375) +- [ ] Low: Standardize inconsistent payload structures across actions diff --git a/prd/components.md b/prd/components.md new file mode 100644 index 000000000..ccb2a557b --- /dev/null +++ b/prd/components.md @@ -0,0 +1,17 @@ +# Components PRD + +**Source Report:** [reports/components-bad-practices.md](../reports/components-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Add missing dependencies to useEffect/useLayoutEffect arrays (AddressList.js: 81, InputNumber.js: 143, ModalConfirmTestnet.js: 17, ModalError.js: 19, NetworkSettingsForm.js: 57, PinPad.js: 115) +- [ ] Critical: Replace jQuery DOM manipulation with React state/refs for modals and animations (GlobalModal.js: 141-145, ModalAddManyTokens.js: 47-71, ModalAddToken.js: 44-68, ModalBackupWords.js: 52-81, ModalConfirm.js: 25-36, RequestError.js: 130-145, SendTokensOne.js: 157-160, OutputsWrapper.js: 48-51, TxData.js: 301, ModalAddressQRCode.js: 60-65) +- [ ] High: Remove console.log/error statements in production (InputNumber.js: 42, TxData.js: 546) +- [ ] High: Memoize props functions with useCallback to prevent re-renders (AddressList.js: 165, CopyButton.js: 22, ChoosePassword.js: 51, ChoosePin.js: 41, Navigation.js: 41, NetworkSettingsForm.js: 92, PinPasswordWrapper.js: 55, TokenBar.js: 158) +- [ ] High: Replace array index as key in lists with unique identifiers (ModalBackupWords.js: 230, SendTokensOne.js: 177, TxData.js: 506, 895) +- [ ] High: Extract inline object/array creation to useMemo or constants (GlobalModal.js: 225, SendTokensOne.js: 177, ModalBackupWords.js: 264-290) +- [ ] Medium: Implement proper cleanup for event listeners in effects (NetworkSettingsForm.js: 47-57, WalletAddress.js: 49-57) +- [ ] Medium: Fix direct state/instance variable mutations (TxData.js: 88, ModalBackupWords.js: 160-162, SendTokensOne.js: 81-83) +- [ ] Low: Split large components into smaller, focused components (TxData.js, ModalBackupWords.js, GlobalModal.js) diff --git a/prd/infrastructure.md b/prd/infrastructure.md new file mode 100644 index 000000000..28ba8e522 --- /dev/null +++ b/prd/infrastructure.md @@ -0,0 +1,15 @@ +# Infrastructure PRD + +**Source Report:** Technical debt identified during setup. + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [x] **Setup Test Environment (Prerequisite)**: + - Add `@testing-library/react`, `@testing-library/jest-dom`, and `redux-saga-test-plan` to devDependencies. + - Configure `src/setupTests.js` to include standard matchers. + - Create a `src/test-utils.js` (or similar) that provides a custom `render` function wrapping components in a `Provider` with a real or mocked store. + - Ensure `npm test` works for both components and sagas. + - Create a sample test for a Reducer, a Component, and a Saga to verify the setup. + - **Note**: This task must be completed first. All other refactoring tasks should be based on the branch created for this task. diff --git a/prd/modules.md b/prd/modules.md new file mode 100644 index 000000000..2d563e12f --- /dev/null +++ b/prd/modules.md @@ -0,0 +1,20 @@ +# Modules and General PRD + +**Source Report:** [reports/modules-bad-practices.md](../reports/modules-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Fix double dispatch bug in wallet saga (sagas/wallet.js: 281) +- [ ] High: Remove all console.log/error/debug statements in production (sagas/wallet.js, sagas/helpers.js, sagas/featureToggle.js, sagas/tokens.js, screens/StartHardwareWallet.js, components/GlobalModal.js, components/InputNumber.js) +- [ ] High: Refactor global singletons to use dependency injection or context (modules/wallet.js, modules/reown.js, modules/unleash.js) +- [ ] High: Move hardcoded magic numbers to constants.js (sagas/featureToggle.js: 43-44, sagas/tokens.js: 36-37, components/GlobalModal.js: 150, 155, 175) +- [ ] Medium: Fix memory leaks by adding proper cleanup to event listeners and timers (components/GlobalModal.js, components/InputNumber.js, screens/StartHardwareWallet.js, components/ModalAddressQRCode.js) +- [ ] Medium: Address security concerns by moving secrets to .env (SENTRY_DSN, REOWN_PROJECT_ID in constants.js) +- [ ] Medium: Fix inconsistent error handling and silent errors (sagas/helpers.js: 105, components/InputNumber.js: 42, utils/helpers.js: 318, sagas/reown.js: 442, storage.js: 218-235) +- [ ] Medium: Resolve potential circular dependencies (utils/helpers.js -> store -> reducers, sagas/wallet.js -> helpers -> store) +- [ ] Low: Add JSDoc documentation and type annotations (modules/reown.js, modules/wallet.js, modules/unleash.js) +- [ ] Low: Properly await Promises in async operations (utils/helpers.js: 116-122) +- [ ] Low: Move translation function usage after initialization (constants.js: 46) +- [ ] Low: Address all TODO/FIXME/XXX comments across the codebase diff --git a/prd/nano-reown.md b/prd/nano-reown.md new file mode 100644 index 000000000..178963f5f --- /dev/null +++ b/prd/nano-reown.md @@ -0,0 +1,18 @@ +# Nano Contract and Reown Components PRD + +**Source Report:** [reports/components-nano-reown-bad-practices.md](../reports/components-nano-reown-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Add missing dependencies to useEffect arrays (ModalChangeAddress.js: 38-52, ModalConfirmUnregister.js: 15-20, ModalSelectAddressToSignData.js: 44-52, ModalSelectAddressToSignTx.js: 26-33, ModalRegisterNanoContract.js: 38-50, ReownModal.js: 32-34, FeedbackModal.js: 43-45, GenericErrorFeedbackModal.js: 24-26) +- [ ] Critical: Replace jQuery DOM manipulation with React state/refs (ModalChangeAddress.js: 39-44, ModalConfirmUnregister.js: 16-19, ModalSelectAddressToSignData.js: 45-51, ModalSelectAddressToSignTx.js: 27-32, ModalRegisterNanoContract.js: 39-49) +- [ ] High: Replace array index as key in lists with unique identifiers (GetBalanceModal.js: 41-59, SignedDataDisplay.js: 23-26, SendTransactionModal.js: 130, 166) +- [ ] High: Wrap prop functions with useCallback (ModalChangeAddress.js, ModalSelectAddressToSignData.js, ModalSelectAddressToSignTx.js, BaseNanoContractModal.js, SendTransactionModal.js) +- [ ] High: Remove console statements in production (CreateTokenRequestData.js: 73, NanoContractActions.js: 95, ErrorDetailModal.js: 41, CreateTokenModal.js: 39) +- [ ] Medium: Extract inline object/array creation to useMemo or constants (ModalChangeAddress.js, ModalSelectAddressToSignData.js, BaseNanoContractModal.js, DAppInfo.js, CreateTokenRequestData.js, ErrorDetailModal.js) +- [ ] Medium: Split large components into smaller ones (BaseNanoContractModal.js, SendTransactionModal.js, NanoContractHistory.js) +- [ ] Medium: Add missing PropTypes to components +- [ ] Medium: Memoize expensive computations with useMemo (NanoContractActions.js, SendTransactionModal.js, BaseNanoContractModal.js) +- [ ] Medium: Replace hardcoded strings with shared constants (NanoContractHistory.js, BaseNanoContractModal.js) diff --git a/prd/reducers.md b/prd/reducers.md new file mode 100644 index 000000000..d506c092e --- /dev/null +++ b/prd/reducers.md @@ -0,0 +1,16 @@ +# Reducers PRD + +**Source Report:** [reports/reducers-bad-practices.md](../reports/reducers-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Fix parameter name bug in onNanoContractDetailLoaded (index.js: 1485) +- [ ] Critical: Move side effects (lockWalletPromise) from reducer to saga (index.js: 707) +- [ ] High: Complete migration of hardcoded action types to types.* constants (index.js: 317-374) +- [ ] High: Add null checks for array access and undefined data fields (index.js: 547-552, 1223) +- [ ] Medium: Replace Object.assign pattern with spread operator (index.js: 318-375) +- [ ] Medium: Replace delete operator with destructuring or spread (index.js: 643, 651, 1090) +- [ ] Medium: Add action payload validation and type checking +- [ ] Low: Standardize return styles (implicit vs explicit) across reducer functions diff --git a/prd/root-files.md b/prd/root-files.md new file mode 100644 index 000000000..741f486ec --- /dev/null +++ b/prd/root-files.md @@ -0,0 +1,21 @@ +# Root Files PRD + +**Source Report:** [reports/root-files-bad-practices.md](../reports/root-files-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Fix error state logic bug in ErrorWrapper (ErrorWrapper.js: 46) +- [ ] High: Add missing dependencies to useEffect hooks (App.js: 182, ErrorWrapper.js: 33) +- [ ] High: Replace jQuery modal manipulation with React state (ErrorWrapper.js: 47) +- [ ] High: Add null checks for DOM elements and context (App.js: 113, index.js: 37) +- [ ] High: Move hardcoded secrets and URLs to .env files (constants.js: 80, 210-211, 310) +- [ ] Medium: Wrap application with root-level Error Boundary (index.js) +- [ ] Medium: Document global polyfills for Buffer and process (index.js: 22-26) +- [ ] Medium: Create ROUTES constant object to replace hardcoded strings (App.js) +- [ ] Medium: Simplify and extract logic from complex nested components (App.js: 275-330) +- [ ] Medium: Remove duplicate CSS imports (ErrorWrapper.js: 14-17) +- [ ] Low: Add PropTypes or TypeScript definitions +- [ ] Low: Organize constants into logical groupings and document magic numbers +- [ ] Low: Add proper error handling and recovery to storage operations (storage.js) diff --git a/prd/sagas.md b/prd/sagas.md new file mode 100644 index 000000000..8521f0e8d --- /dev/null +++ b/prd/sagas.md @@ -0,0 +1,19 @@ +# Sagas PRD + +**Source Report:** [reports/sagas-bad-practices.md](../reports/sagas-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Fix typo in property name 'falure' -> 'failure' (featureToggle.js: 78) +- [ ] Critical: Define missing 'newWsServer' variable (networkSettings.js: 39) +- [ ] Critical: Fix double dispatch yield put(dispatch(...)) (wallet.js: 281) +- [ ] High: Remove console.log/debug statements (featureToggle.js, wallet.js, tokens.js, nanoContract.js) +- [ ] High: Properly yield blocking operations with yield call() (tokens.js: 365, nanoContract.js: 365) +- [ ] High: Move hardcoded error codes to constants (reown.js: 442+) +- [ ] Medium: Replace async/await with Redux-Saga effects (reown.js: 594, 831) +- [ ] Medium: Add cancellation checks to infinite loops (nanoContract.js: 180) +- [ ] Medium: Remove unnecessary parenthesis in yield put (atomicSwap.js: 149) +- [ ] Low: Fix typos in comments and improve error context in actions +- [ ] Low: Standardize logging using the logger module instead of console diff --git a/prd/screens.md b/prd/screens.md new file mode 100644 index 000000000..c31325d8e --- /dev/null +++ b/prd/screens.md @@ -0,0 +1,18 @@ +# Screens PRD + +**Source Report:** [reports/screens-bad-practices.md](../reports/screens-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Memoize functions passed to children with useCallback (SendTokens.js: 522-525, 535-553, ReownConnect.js: 44, 88, 110, 146, LockedWallet.js: 120, Settings.js: 289) +- [ ] Critical: Fix missing dependencies in useEffect hooks (LoadingAddresses.js: 68, NFTList.js: 76, UnknownTokens.js: 79, Wallet.js: 92, 103, TransactionDetail.js: 120, LoadWalletFailed.js: 37) +- [ ] Critical: Replace React.createRef() calls during render with useRef() (NFTList.js: 104, UnknownTokens.js: 104, Welcome.js: 34, SendTokens.js: 77, 485) +- [ ] Critical: Replace jQuery DOM manipulation/animations with React state (ChoosePassphrase.js: 113-117, CreateNFT.js: 227-231, CreateToken.js: 198-203, UnknownTokens.js) +- [ ] High: Extract inline object/array creation in props to useMemo or constants (CustomTokens.js: 45-50, CreateNFT.js: 281-284, CreateToken.js: 251-257, Settings.js: 151-168, 176-193, Welcome.js: 86-92) +- [ ] High: Extract inline style objects to CSS or constants (CreateNFT.js: 327, CreateToken.js: 288, Wallet.js: 327) +- [ ] High: Memoize render functions within components (ChoosePassphrase.js: 121, 132, UnknownTokens.js: 203-247, Wallet.js: 209-216, 235-255) +- [ ] Medium: Refactor complex state update logic (TransactionDetail.js: 73-91, Wallet.js: 108-125) +- [ ] Medium: Avoid hardcoded object creation in hot paths (SendTokens.js: 188) +- [ ] Low: Split large screen components into smaller sub-components diff --git a/prd/tokens-atomic-swap.md b/prd/tokens-atomic-swap.md new file mode 100644 index 000000000..80f676d2b --- /dev/null +++ b/prd/tokens-atomic-swap.md @@ -0,0 +1,18 @@ +# Tokens and Atomic Swap Components PRD + +**Source Report:** [reports/components-tokens-atomicswap-bad-practices.md](../reports/components-tokens-atomicswap-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Replace jQuery animations with React state for visibility (TokenMint.js: 10, 120, 122) +- [ ] Critical: Replace array index as key in lists with unique identifiers (ProposalBalanceTable.js: 45, ModalAtomicSend.js: 118) +- [ ] High: Replace deprecated string refs with React.createRef() (TokenAction.js: 60) +- [ ] High: Add missing dependencies to useEffect (ExternalChangeModal.js: 16, ModalAtomicReceive.js: 77, ModalAtomicSend.js: 285) +- [ ] High: Fix async validation bug by adding await (ModalAtomicReceive.js: 65) +- [ ] Medium: Wrap inline handlers and callbacks with useCallback (ExternalChangeModal.js: 24, ModalAtomicSend.js: 120, 125) +- [ ] Medium: Extract render functions and memoize them (TokenMelt.js: 101, TokenMint.js: 134-160, ProposalBalanceTable.js: 26-56, ModalAtomicReceive.js: 26-30) +- [ ] Medium: Memoize array filter operations with useMemo (ProposalBalanceTable.js: 19-23) +- [ ] Low: Extract inline style objects to constants (TokenMint.js: 145) +- [ ] Low: Use boolean for defaultChecked and avoid hardcoded IDs (ModalAtomicSend.js: 340) diff --git a/prd/utils.md b/prd/utils.md new file mode 100644 index 000000000..a6621b7f5 --- /dev/null +++ b/prd/utils.md @@ -0,0 +1,18 @@ +# Utils PRD + +**Source Report:** [reports/utils-bad-practices.md](../reports/utils-bad-practices.md) + +## Common Directives +- [ ] **Test-Driven Refactoring**: For every task, create or update unit tests to verify the fix and prevent regressions. + +## Tasks +- [ ] Critical: Fix shadowed variable isAllAuthority (helpers.js: 313) +- [ ] High: Remove console.error/log statements (wallet.js: 127, 192, helpers.js: 318) +- [ ] High: Address all TODO/FIXME/XXX technical debt comments (wallet.js, ledger.js, atomicSwap.js) +- [ ] High: Add input validation to public utility functions (nanoContracts.js, atomicSwap.js, helpers.js, i18n.js, wallet.js) +- [ ] Medium: Implement proper error handling for async operations and Promise.all (wallet.js: 151-159, 178, atomicSwap.js: 233-239, ledger.js: 177-185) +- [ ] Medium: Fix potential race conditions by awaiting promises (helpers.js: 120) +- [ ] Medium: Extract magic numbers to constants (ledger.js, nanoContracts.js, helpers.js) +- [ ] Medium: Sanitize inputs in JSX-returning functions to prevent XSS (nanoContracts.js: 96, i18n.js: 33) +- [ ] Low: Add missing JSDoc comments and return types (nanoContracts.js, atomicSwap.js, storage.js) +- [ ] Low: Fix redundant conditional logic and improve error recovery (helpers.js, wallet.js, atomicSwap.js) diff --git a/src/App.js b/src/App.js index 5ccf2a98f..87166bb69 100644 --- a/src/App.js +++ b/src/App.js @@ -44,11 +44,10 @@ import { useDispatch, useSelector } from 'react-redux'; import { GlobalModalContext, MODAL_TYPES } from './components/GlobalModal'; import createRequestInstance from './api/axiosInstance'; import hathorLib from '@hathor/wallet-lib'; -import { IPC_RENDERER, NETWORK_SETTINGS } from './constants'; +import { IPC_RENDERER, NETWORK_SETTINGS, WALLET_STATUS } from './constants'; import AllAddresses from './screens/AllAddresses'; import NFTList from './screens/NFTList'; import { resetNavigateTo, updateLedgerClosed } from './actions/index'; -import { WALLET_STATUS } from './sagas/wallet'; import ProposalList from './screens/atomic-swap/ProposalList'; import EditSwap from './screens/atomic-swap/EditSwap'; import NewSwap from './screens/atomic-swap/NewSwap'; diff --git a/src/__tests__/components/ModalPin.test.js b/src/__tests__/components/ModalPin.test.js index 0b683b689..ce0f7ad7a 100644 --- a/src/__tests__/components/ModalPin.test.js +++ b/src/__tests__/components/ModalPin.test.js @@ -4,8 +4,11 @@ import { act, render, screen } from '@testing-library/react'; import $ from 'jquery' import { ModalPin } from '../../components/ModalPin'; import userEvent from '@testing-library/user-event'; +import { getGlobalWallet } from '../../modules/wallet'; // import '@testing-library/jest-dom'; Temporarily removed. See https://github.com/HathorNetwork/hathor-wallet/pull/567 +jest.mock('../../modules/wallet'); + let container = null; const MODAL_ID = '#modalPin'; @@ -61,6 +64,7 @@ describe('pin validation', () => { const failingPin = 'abc123'; const passingPin = '123321' const wallet = { checkPin: () => Promise.resolve(false) }; + getGlobalWallet.mockReturnValue(wallet); render( { it('displays error on incorrect pin', async () => { const wallet = { checkPin: () => Promise.resolve(false) }; + getGlobalWallet.mockReturnValue(wallet); render( { const closeCallback = jest.fn(); const pinText = '123321'; const wallet = { checkPin: () => Promise.resolve(true) }; + getGlobalWallet.mockReturnValue(wallet); render(
Test Component
; + +describe('Infrastructure Check', () => { + test('Component render', () => { + render(); + expect(screen.getByText('Test Component')).toBeInTheDocument(); + }); + + test('Reducer initial state', () => { + const initialState = rootReducer(undefined, { type: '@@INIT' }); + expect(initialState).toHaveProperty('tokens'); + }); + + test('Saga test', () => { + function* sampleSaga() { + yield 1; + return 1; + } + return expectSaga(sampleSaga) + .returns(1) + .run(); + }); +}); diff --git a/src/__tests__/utils/atomicSwap.test.js b/src/__tests__/utils/atomicSwap.test.js index 8140ccfc2..dce033426 100644 --- a/src/__tests__/utils/atomicSwap.test.js +++ b/src/__tests__/utils/atomicSwap.test.js @@ -1,4 +1,5 @@ -import { calculateExhibitionData, PROPOSAL_DOWNLOAD_STATUS } from "../../utils/atomicSwap"; +import { calculateExhibitionData } from "../../utils/atomicSwap"; +import { PROPOSAL_DOWNLOAD_STATUS } from "../../constants"; import { PartialTxProposal } from "@hathor/wallet-lib"; const customTokenUid = '00003b47ce1a6774cfc132169122c38c15fbc4a7f43487cf1041ff4826c1842e'; @@ -36,7 +37,7 @@ function createNewProposal() { hash: "000000e5924f0b07a626fd47839f85983a0faf14a337ac85e53cc6bb877bd14a", index: 0, data: null, - value: 6400, + value: 6400n, authorities: 0, token: "00", address: "other-1" @@ -44,7 +45,7 @@ function createNewProposal() { ] np.partialTx.outputs = [ { - value: 6200, + value: 6200n, tokenData: 0, decodedScript: { address: { base58: "other-2" }, @@ -55,7 +56,7 @@ function createNewProposal() { authorities: 0 }, { - value: 1, + value: 1n, tokenData: 1, decodedScript: { address: { base58: "other-2" }, @@ -86,7 +87,7 @@ describe('calculateExhibitionData', () => { deserializeSpy.mockImplementationOnce(() => { const np = createNewProposal(); np.partialTx.outputs.push({ - value: 200, + value: 200n, tokenData: 0, decodedScript: { address: { base58: "mine-1" }, @@ -104,7 +105,7 @@ describe('calculateExhibitionData', () => { expect(results).toStrictEqual([ expect.objectContaining({ tokenUid: '00', - receiving: 200, + receiving: 200n, }) ]); }) @@ -117,7 +118,7 @@ describe('calculateExhibitionData', () => { hash: "000000e5924f0b07a626fd47839f85983a0faf14a337ac85e53cc6bb877bd14a", index: 0, data: null, - value: 1, + value: 1n, authorities: 0, token: customTokenUid, address: "mine-1", @@ -130,7 +131,7 @@ describe('calculateExhibitionData', () => { expect(results).toStrictEqual([ expect.objectContaining({ tokenUid: customTokenUid, - sending: 1, + sending: 1n, }) ]); }) @@ -143,14 +144,14 @@ describe('calculateExhibitionData', () => { hash: "000000e5924f0b07a626fd47839f85983a0faf14a337ac85e53cc6bb877bd14a", index: 0, data: null, - value: 1, + value: 1n, authorities: 0, token: customTokenUid, address: "mine-1", isAuthority: () => false, }) np.partialTx.outputs.push({ - value: 200, + value: 200n, tokenData: 0, decodedScript: { address: { base58: "mine-1" }, @@ -168,11 +169,11 @@ describe('calculateExhibitionData', () => { expect(results).toStrictEqual(expect.arrayContaining([ expect.objectContaining({ tokenUid: customTokenUid, - sending: 1, + sending: 1n, }), expect.objectContaining({ tokenUid: '00', - receiving: 200, + receiving: 200n, }) ])); }) @@ -185,14 +186,14 @@ describe('calculateExhibitionData', () => { hash: "000000e5924f0b07a626fd47839f85983a0faf14a337ac85e53cc6bb877bd14a", index: 0, data: null, - value: 1, + value: 1n, authorities: 0, token: '00', address: "mine-1", isAuthority: () => false, }) np.partialTx.outputs.push({ - value: 1, + value: 1n, tokenData: 0, decodedScript: { address: { base58: "mine-2" }, @@ -222,14 +223,14 @@ describe('calculateExhibitionData', () => { hash: "000000e5924f0b07a626fd47839f85983a0faf14a337ac85e53cc6bb877bd14a", index: 0, data: null, - value: 1, + value: 1n, authorities: 0, token: '00', address: "mine-1", isAuthority: () => false, }) np.partialTx.outputs.push({ - value: 1, + value: 1n, tokenData: 0, decodedScript: { address: { base58: "mine-2" }, @@ -246,7 +247,7 @@ describe('calculateExhibitionData', () => { hash: "000000e5924f0b07a626fd47839f85983a0faf14a337ac85e53cc6bb877bd14a", index: 0, data: null, - value: 2, + value: 2n, authorities: 0, token: 'fake1', address: "mine-3", @@ -255,7 +256,7 @@ describe('calculateExhibitionData', () => { // Token 'fake2' has receiving balance np.partialTx.outputs.push({ - value: 3, + value: 3n, tokenData: 0, decodedScript: { address: { base58: "mine-4" }, @@ -272,14 +273,14 @@ describe('calculateExhibitionData', () => { hash: "000000e5924f0b07a626fd47839f85983a0faf14a337ac85e53cc6bb877bd14a", index: 0, data: null, - value: 4, + value: 4n, authorities: 0, token: 'fake3', address: "other-1", isAuthority: () => false, }) np.partialTx.outputs.push({ - value: 3, + value: 3n, tokenData: 0, decodedScript: { address: { base58: "other-2" }, @@ -301,11 +302,11 @@ describe('calculateExhibitionData', () => { }), expect.objectContaining({ tokenUid: 'fake1', - sending: 2 + sending: 2n }), expect.objectContaining({ tokenUid: 'fake2', - receiving: 3 + receiving: 3n }), ]); expect(results).not.toContain( diff --git a/src/components/NFTListElement.js b/src/components/NFTListElement.js index f0844c697..f041277f0 100644 --- a/src/components/NFTListElement.js +++ b/src/components/NFTListElement.js @@ -12,7 +12,7 @@ import { VIDEO_MEDIA_TYPES_BY_EXTENSION, AUDIO_MEDIA_TYPES_BY_EXTENSION, } from '../constants'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../constants'; import helpers from '../utils/helpers'; import Loading from '../components/Loading'; import { numberUtils } from '@hathor/wallet-lib'; diff --git a/src/components/SendTokensOne.js b/src/components/SendTokensOne.js index f23e53220..6b9014cb8 100644 --- a/src/components/SendTokensOne.js +++ b/src/components/SendTokensOne.js @@ -18,7 +18,7 @@ import version from '../utils/version'; import OutputsWrapper from '../components/OutputsWrapper'; import InputsWrapper from '../components/InputsWrapper'; import Loading from '../components/Loading'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../constants'; import LOCAL_STORE from '../storage'; diff --git a/src/components/TokenAdministrative.js b/src/components/TokenAdministrative.js index 7e0af895a..64f1ebe5d 100644 --- a/src/components/TokenAdministrative.js +++ b/src/components/TokenAdministrative.js @@ -18,7 +18,7 @@ import TokenDelegate from '../components/tokens/TokenDelegate'; import TokenDestroy from '../components/tokens/TokenDestroy'; import Loading from '../components/Loading'; import helpers from '../utils/helpers'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../constants'; import LOCAL_STORE from '../storage'; const mapStateToProps = (state) => { diff --git a/src/components/TokenBar.js b/src/components/TokenBar.js index 7dfef8e15..a3f88abc3 100644 --- a/src/components/TokenBar.js +++ b/src/components/TokenBar.js @@ -14,7 +14,7 @@ import { get } from 'lodash'; import helpers from '../utils/helpers'; import wallet from "../utils/wallet"; import Loading from '../components/Loading'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../constants'; import { useLocation, useNavigate } from 'react-router-dom'; import LOCAL_STORE from '../storage'; diff --git a/src/components/TokenHistory.js b/src/components/TokenHistory.js index 7bfd71224..5dbc0c7ea 100644 --- a/src/components/TokenHistory.js +++ b/src/components/TokenHistory.js @@ -18,7 +18,7 @@ import helpers from '../utils/helpers'; import { colors } from '../constants'; import TokenPagination from './TokenPagination'; import HathorAlert from './HathorAlert'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../constants'; import { getGlobalWallet } from "../modules/wallet"; const mapStateToProps = (state, props) => { diff --git a/src/components/nano-contract/ModalRegisterNanoContract.js b/src/components/nano-contract/ModalRegisterNanoContract.js index f6191c396..a8b462d15 100644 --- a/src/components/nano-contract/ModalRegisterNanoContract.js +++ b/src/components/nano-contract/ModalRegisterNanoContract.js @@ -12,8 +12,7 @@ import { get } from 'lodash'; import { useDispatch, useSelector } from 'react-redux'; import { cleanNanoContractRegisterMetadata, registerNanoContract } from '../../actions/index'; import { getGlobalWallet } from "../../modules/wallet"; -import { NANOCONTRACT_REGISTER_STATUS } from '../../sagas/nanoContract'; -import { colors } from '../../constants'; +import { NANOCONTRACT_REGISTER_STATUS, colors } from '../../constants'; import ReactLoading from 'react-loading'; /** diff --git a/src/components/tokens/TokenMelt.js b/src/components/tokens/TokenMelt.js index eabf7c739..4fb39a031 100644 --- a/src/components/tokens/TokenMelt.js +++ b/src/components/tokens/TokenMelt.js @@ -15,7 +15,7 @@ import InputNumber from '../InputNumber'; import ReactLoading from 'react-loading'; import { connect } from 'react-redux'; import { get } from 'lodash'; -import { TOKEN_DOWNLOAD_STATUS } from '../../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../../constants'; import { colors } from '../../constants'; import { getGlobalWallet } from "../../modules/wallet"; diff --git a/src/constants.js b/src/constants.js index 7513173eb..6d00bd82d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -318,3 +318,42 @@ export const REOWN_CONNECTION_STATE = { SUCCESS: 'success', // Connection successful FAILED: 'failed', // Connection failed }; + +/** + * Token download status + */ +export const TOKEN_DOWNLOAD_STATUS = { + READY: 'ready', + FAILED: 'failed', + LOADING: 'loading', + INVALIDATED: 'invalidated', +}; + +/** + * Wallet status + */ +export const WALLET_STATUS = { + READY: 'ready', + FAILED: 'failed', + LOADING: 'loading', + SYNCING: 'syncing', +}; + +/** + * Proposal download status + */ +export const PROPOSAL_DOWNLOAD_STATUS = { + READY: 'ready', + FAILED: 'failed', + LOADING: 'loading', + INVALIDATED: 'invalidated', +}; + +/** + * Nano Contract register status + */ +export const NANOCONTRACT_REGISTER_STATUS = { + LOADING: 'loading', + ERROR: 'error', + SUCCESS: 'success', +}; diff --git a/src/reducers/index.js b/src/reducers/index.js index a675904bc..c51073a62 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -5,15 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -import { FEATURE_TOGGLE_DEFAULTS, NANO_CONTRACT_DETAIL_STATUS, NETWORK_SETTINGS, NETWORK_SETTINGS_STATUS, VERSION } from '../constants'; +import { FEATURE_TOGGLE_DEFAULTS, NANO_CONTRACT_DETAIL_STATUS, NETWORK_SETTINGS, NETWORK_SETTINGS_STATUS, VERSION, TOKEN_DOWNLOAD_STATUS, WALLET_STATUS, PROPOSAL_DOWNLOAD_STATUS, NANOCONTRACT_REGISTER_STATUS } from '../constants'; import { types } from '../actions'; import { get, findIndex } from 'lodash'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; -import { WALLET_STATUS } from '../sagas/wallet'; -import { NANOCONTRACT_REGISTER_STATUS } from '../sagas/nanoContract'; -import { PROPOSAL_DOWNLOAD_STATUS } from '../utils/atomicSwap'; -import { constants as hathorLibConstants } from "@hathor/wallet-lib"; -import helpersUtils from '../utils/helpers'; +import { constants as hathorLibConstants, transactionUtils } from "@hathor/wallet-lib"; import LOCAL_STORE from '../storage'; import reownReducer from './reown'; @@ -493,6 +488,22 @@ const rootReducer = (state = initialState, action) => { } }; +const isAllAuthority = (tx) => { + for (let txin of tx.inputs) { + if (!transactionUtils.isAuthorityOutput(txin)) { + return false; + } + } + + for (let txout of tx.outputs) { + if (!transactionUtils.isAuthorityOutput(txout)) { + return false; + } + } + + return true; +}; + const getTxHistoryFromWSTx = (tx, tokenUid, tokenTxBalance) => { return { tx_id: tx.tx_id, @@ -501,7 +512,7 @@ const getTxHistoryFromWSTx = (tx, tokenUid, tokenTxBalance) => { balance: tokenTxBalance, is_voided: tx.is_voided, version: tx.version, - isAllAuthority: helpersUtils.isAllAuthority(tx), + isAllAuthority: isAllAuthority(tx), } }; diff --git a/src/sagas/atomicSwap.js b/src/sagas/atomicSwap.js index 25c210395..57caedbb6 100644 --- a/src/sagas/atomicSwap.js +++ b/src/sagas/atomicSwap.js @@ -22,8 +22,8 @@ import { ATOMIC_SWAP_SERVICE_ERRORS, generateReduxObjFromProposal, updatePersistentStorage, - PROPOSAL_DOWNLOAD_STATUS, } from "../utils/atomicSwap"; +import { PROPOSAL_DOWNLOAD_STATUS } from "../constants"; import { t } from "ttag"; import { swapService } from '@hathor/wallet-lib' import { getGlobalWallet } from "../modules/wallet"; diff --git a/src/sagas/nanoContract.js b/src/sagas/nanoContract.js index 9dc3301b6..45eed1bcc 100644 --- a/src/sagas/nanoContract.js +++ b/src/sagas/nanoContract.js @@ -18,16 +18,10 @@ import { types, } from '../actions'; -import { NANO_CONTRACT_DETAIL_STATUS } from '../constants'; +import { NANO_CONTRACT_DETAIL_STATUS, NANOCONTRACT_REGISTER_STATUS } from '../constants'; import { all, call, delay, put, select, takeEvery } from 'redux-saga/effects'; -export const NANOCONTRACT_REGISTER_STATUS = { - LOADING: 'loading', - ERROR: 'error', - SUCCESS: 'success', -}; - const NANOCONTRACT_WAIT_TX_CONFIRMED_DELAY = 5000; /** diff --git a/src/sagas/tokens.js b/src/sagas/tokens.js index a1055e4c4..019546501 100644 --- a/src/sagas/tokens.js +++ b/src/sagas/tokens.js @@ -15,7 +15,7 @@ import { channel } from 'redux-saga'; import { get } from 'lodash'; import { specificTypeAndPayload, dispatchAndWait } from './helpers'; import helpers from '../utils/helpers'; -import { METADATA_CONCURRENT_DOWNLOAD } from '../constants'; +import { METADATA_CONCURRENT_DOWNLOAD, TOKEN_DOWNLOAD_STATUS } from '../constants'; import { types, tokenFetchBalanceRequested, @@ -38,13 +38,6 @@ const METADATA_MAX_RETRIES = 3; const log = logger('tokens'); -export const TOKEN_DOWNLOAD_STATUS = { - READY: 'ready', - FAILED: 'failed', - LOADING: 'loading', - INVALIDATED: 'invalidated', -}; - /** * This saga will create a channel to queue TOKEN_FETCH_BALANCE_REQUESTED actions and * consumers that will run in parallel consuming those actions. diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index ea8da450c..38b8a9e64 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -28,6 +28,7 @@ import { WALLET_SERVICE_FEATURE_TOGGLE, ATOMIC_SWAP_SERVICE_FEATURE_TOGGLE, IGNORE_WS_TOGGLE_FLAG, + WALLET_STATUS, } from '../constants'; import { types, @@ -76,13 +77,6 @@ import { initializeSwapServiceBaseUrlForWallet } from "../utils/atomicSwap"; import { getGlobalWallet, setGlobalWallet } from "../modules/wallet"; import { isEmpty } from 'lodash'; -export const WALLET_STATUS = { - READY: 'ready', - FAILED: 'failed', - LOADING: 'loading', - SYNCING: 'syncing', -}; - export function* isWalletServiceEnabled() { const shouldIgnoreFlag = localStorage.getItem(IGNORE_WS_TOGGLE_FLAG); diff --git a/src/screens/NFTList.js b/src/screens/NFTList.js index d72ec4d0f..df5626b20 100644 --- a/src/screens/NFTList.js +++ b/src/screens/NFTList.js @@ -10,7 +10,7 @@ import { t } from 'ttag' import { get } from 'lodash'; import { useSelector } from 'react-redux'; import { NFT_LIST_PER_PAGE } from '../constants'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../constants'; import HathorPaginate from '../components/HathorPaginate'; import NFTListElement from '../components/NFTListElement'; import BackButton from '../components/BackButton'; diff --git a/src/screens/UnknownTokens.js b/src/screens/UnknownTokens.js index f03bd39c9..f5887024a 100644 --- a/src/screens/UnknownTokens.js +++ b/src/screens/UnknownTokens.js @@ -15,7 +15,7 @@ import HathorAlert from '../components/HathorAlert'; import TokenHistory from '../components/TokenHistory'; import BackButton from '../components/BackButton'; import { tokenFetchBalanceRequested, tokenFetchHistoryRequested } from '../actions'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../constants'; import { WALLET_HISTORY_COUNT } from '../constants'; import { GlobalModalContext, MODAL_TYPES } from '../components/GlobalModal'; import helpers from '../utils/helpers'; diff --git a/src/screens/Wallet.js b/src/screens/Wallet.js index 6531b69b4..c91b43648 100644 --- a/src/screens/Wallet.js +++ b/src/screens/Wallet.js @@ -24,7 +24,7 @@ import version from '../utils/version'; import walletUtils from '../utils/wallet'; import BackButton from '../components/BackButton'; import { colors } from '../constants'; -import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens'; +import { TOKEN_DOWNLOAD_STATUS } from '../constants'; import { GlobalModalContext, MODAL_TYPES } from '../components/GlobalModal'; import { tokenFetchBalanceRequested, tokenFetchHistoryRequested } from '../actions/index'; import LOCAL_STORE from '../storage'; diff --git a/src/screens/atomic-swap/ImportExisting.js b/src/screens/atomic-swap/ImportExisting.js index 60e4716c7..a6154208f 100644 --- a/src/screens/atomic-swap/ImportExisting.js +++ b/src/screens/atomic-swap/ImportExisting.js @@ -12,9 +12,9 @@ import { useNavigate } from "react-router-dom"; import { useDispatch, useSelector } from "react-redux"; import { importProposal, proposalFetchRequested } from "../../actions"; import { - PROPOSAL_DOWNLOAD_STATUS, updatePersistentStorage } from "../../utils/atomicSwap"; +import { PROPOSAL_DOWNLOAD_STATUS } from "../../constants"; export default function ImportExisting(props) { // Internal state diff --git a/src/screens/atomic-swap/ProposalList.js b/src/screens/atomic-swap/ProposalList.js index c5b5fae0a..da1a5696d 100644 --- a/src/screens/atomic-swap/ProposalList.js +++ b/src/screens/atomic-swap/ProposalList.js @@ -13,9 +13,9 @@ import { useNavigate } from 'react-router-dom'; import Loading from "../../components/Loading"; import { proposalFetchRequested, proposalRemoved } from "../../actions"; import { - PROPOSAL_DOWNLOAD_STATUS, updatePersistentStorage } from "../../utils/atomicSwap"; +import { PROPOSAL_DOWNLOAD_STATUS } from "../../constants"; import walletUtil from "../../utils/wallet"; import { GlobalModalContext, MODAL_TYPES } from '../../components/GlobalModal'; diff --git a/src/setupTests.js b/src/setupTests.js new file mode 100644 index 000000000..fe868c20f --- /dev/null +++ b/src/setupTests.js @@ -0,0 +1,10 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; +import { TextEncoder, TextDecoder } from 'util'; + +global.TextEncoder = TextEncoder; +global.TextDecoder = TextDecoder; + diff --git a/src/test-utils.js b/src/test-utils.js new file mode 100644 index 000000000..3ca0d9342 --- /dev/null +++ b/src/test-utils.js @@ -0,0 +1,34 @@ +import React from 'react'; +import { render as rtlRender } from '@testing-library/react'; +import { configureStore } from '@reduxjs/toolkit'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; +import rootReducer from './reducers'; +import thunk from 'redux-thunk'; + +function render( + ui, + { + preloadedState, + store = configureStore({ + reducer: rootReducer, + preloadedState, + middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false }).concat(thunk) + }), + ...renderOptions + } = {} +) { + function Wrapper({ children }) { + return ( + + + {children} + + + ); + } + return rtlRender(ui, { wrapper: Wrapper, ...renderOptions }); +} + +export * from '@testing-library/react'; +export { render }; diff --git a/src/utils/atomicSwap.js b/src/utils/atomicSwap.js index 0f66d32fa..e4775a1d2 100644 --- a/src/utils/atomicSwap.js +++ b/src/utils/atomicSwap.js @@ -16,6 +16,7 @@ import { import { TOKEN_MINT_MASK, TOKEN_MELT_MASK, NATIVE_TOKEN_UID } from "@hathor/wallet-lib/lib/constants"; import { get } from 'lodash'; import walletUtil from "./wallet"; +import { PROPOSAL_DOWNLOAD_STATUS } from "../constants"; /** * @typedef ProposalData @@ -50,13 +51,6 @@ import walletUtil from "./wallet"; * @property {{partialTx: string, timestamp: number}[]} history Historic data */ -export const PROPOSAL_DOWNLOAD_STATUS = { - READY: 'ready', - FAILED: 'failed', - LOADING: 'loading', - INVALIDATED: 'invalidated', -}; - export const PROPOSAL_SIGNATURE_STATUS = { OPEN: 'Open', PARTIALLY_SIGNED: 'Partially Signed',