diff --git a/web/cashtab-v2/config/jest/uint8array-environment.js b/web/cashtab-v2/config/jest/uint8array-environment.js new file mode 100644 --- /dev/null +++ b/web/cashtab-v2/config/jest/uint8array-environment.js @@ -0,0 +1,25 @@ +const JSDOMEnvironment = require('jest-environment-jsdom'); + +class CustomEnvironment extends JSDOMEnvironment { + constructor(config, context) { + super(config, context); + this.testPath = context.testPath; + this.docblockPragmas = context.docblockPragmas; + } + + async setup() { + await super.setup(); + this.global.Uint8Array = Uint8Array; + this.global.ArrayBuffer = ArrayBuffer; + } + + async teardown() { + await super.teardown(); + } + + getVmContext() { + return super.getVmContext(); + } +} + +module.exports = CustomEnvironment; diff --git a/web/cashtab-v2/package-lock.json b/web/cashtab-v2/package-lock.json --- a/web/cashtab-v2/package-lock.json +++ b/web/cashtab-v2/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "cashtab-v2", "version": "0.1.0", "dependencies": { "@ant-design/icons": "^4.3.0", @@ -14,6 +13,7 @@ "@svgr/webpack": "^5.5.0", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", + "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", "@zxing/library": "0.8.0", "antd": "^4.9.3", @@ -3628,6 +3628,34 @@ "react-dom": "*" } }, + "node_modules/@testing-library/react-hooks": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz", + "integrity": "sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/react": ">=16.9.0", + "@types/react-dom": ">=16.9.0", + "@types/react-test-renderer": ">=16.9.0", + "react-error-boundary": "^3.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0", + "react-test-renderer": ">=16.9.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-test-renderer": { + "optional": true + } + } + }, "node_modules/@testing-library/user-event": { "version": "13.5.0", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", @@ -3898,6 +3926,14 @@ "@types/react": "*" } }, + "node_modules/@types/react-test-renderer": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz", + "integrity": "sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -4593,6 +4629,8 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -4607,7 +4645,9 @@ "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "optional": true, + "peer": true }, "node_modules/ajv-keywords": { "version": "3.5.2", @@ -16423,6 +16463,21 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" }, + "node_modules/react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-error-overlay": { "version": "6.0.10", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz", @@ -22430,6 +22485,18 @@ "@types/react-dom": "*" } }, + "@testing-library/react-hooks": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz", + "integrity": "sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg==", + "requires": { + "@babel/runtime": "^7.12.5", + "@types/react": ">=16.9.0", + "@types/react-dom": ">=16.9.0", + "@types/react-test-renderer": ">=16.9.0", + "react-error-boundary": "^3.1.0" + } + }, "@testing-library/user-event": { "version": "13.5.0", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", @@ -22687,6 +22754,14 @@ "@types/react": "*" } }, + "@types/react-test-renderer": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz", + "integrity": "sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw==", + "requires": { + "@types/react": "*" + } + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -23220,14 +23295,13 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": { - "ajv": "^8.0.0" - }, + "requires": {}, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "version": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "optional": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -23238,7 +23312,9 @@ "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "optional": true, + "peer": true } } }, @@ -31893,6 +31969,14 @@ } } }, + "react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "requires": { + "@babel/runtime": "^7.12.5" + } + }, "react-error-overlay": { "version": "6.0.10", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz", @@ -33154,7 +33238,6 @@ "is-glob": "^4.0.3", "normalize-path": "^3.0.0", "object-hash": "^2.2.0", - "postcss": "^8.4.6", "postcss-js": "^4.0.0", "postcss-load-config": "^3.1.0", "postcss-nested": "5.0.6", diff --git a/web/cashtab-v2/package.json b/web/cashtab-v2/package.json --- a/web/cashtab-v2/package.json +++ b/web/cashtab-v2/package.json @@ -9,6 +9,7 @@ "@svgr/webpack": "^5.5.0", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", + "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", "@zxing/library": "0.8.0", "antd": "^4.9.3", diff --git a/web/cashtab-v2/src/hooks/__mocks__/mockLegacyWallets.js b/web/cashtab-v2/src/hooks/__mocks__/mockLegacyWallets.js new file mode 100644 --- /dev/null +++ b/web/cashtab-v2/src/hooks/__mocks__/mockLegacyWallets.js @@ -0,0 +1,127 @@ +export default { + legacyAlphaMainnet: { + mnemonic: + 'apart vacuum color cream drama kind foil history hurt alone ask census', + name: 'MigrationTestAlpha on Mainnet', + Path245: { + cashAddress: + 'bitcoincash:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy54hkry298', + slpAddress: + 'simpleledger:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy5evac32me', + fundingWif: 'KwgNkyijAaxFr5XQdnaYyNMXVSZobgHzSoKKfWiC3Q7Xr4n7iYMG', + fundingAddress: + 'simpleledger:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy5evac32me', + legacyAddress: '1EgPUfBgU7ekho3EjtGze87dRADnUE8ojP', + }, + Path145: { + cashAddress: + 'bitcoincash:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9v0lgx569z', + slpAddress: + 'simpleledger:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vryrap6mu', + fundingWif: 'L2xvTe6CdNxroR6pbdpGWNjAa55AZX5Wm59W5TXMuH31ihNJdDjt', + fundingAddress: + 'simpleledger:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vryrap6mu', + legacyAddress: '1511T3ynXKgCwXhFijCUWKuTfqbPxFV1AF', + }, + }, + + migratedLegacyAlphaMainnet: { + mnemonic: + 'apart vacuum color cream drama kind foil history hurt alone ask census', + name: 'MigrationTestAlpha on Mainnet', + Path245: { + cashAddress: + 'bitcoincash:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy54hkry298', + slpAddress: + 'simpleledger:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy5evac32me', + fundingWif: 'KwgNkyijAaxFr5XQdnaYyNMXVSZobgHzSoKKfWiC3Q7Xr4n7iYMG', + fundingAddress: + 'simpleledger:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy5evac32me', + legacyAddress: '1EgPUfBgU7ekho3EjtGze87dRADnUE8ojP', + publicKey: + '03c4a69fd90c8b196683216cffd2943a7b13b0db0812e44a4ff156ac7e03fc4ed7', + }, + Path145: { + cashAddress: + 'bitcoincash:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9v0lgx569z', + slpAddress: + 'simpleledger:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vryrap6mu', + fundingWif: 'L2xvTe6CdNxroR6pbdpGWNjAa55AZX5Wm59W5TXMuH31ihNJdDjt', + fundingAddress: + 'simpleledger:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vryrap6mu', + legacyAddress: '1511T3ynXKgCwXhFijCUWKuTfqbPxFV1AF', + publicKey: + '02fe5308d77bcce825068a9e46adc6f032dbbe39167a7b6d05ac563ac71d8b186e', + }, + Path1899: { + cashAddress: + 'bitcoincash:qzagy47mvh6qxkvcn3acjnz73rkhkc6y7cptzgcqy6', + slpAddress: + 'simpleledger:qzagy47mvh6qxkvcn3acjnz73rkhkc6y7cdsfndq6y', + fundingWif: 'Kx4FiBMvKK1iXjFk5QTaAK6E4mDGPjmwDZ2HDKGUZpE4gCXMaPe9', + fundingAddress: + 'simpleledger:qzagy47mvh6qxkvcn3acjnz73rkhkc6y7cdsfndq6y', + legacyAddress: '1J1Aq5tAAYxZgSDRo8soKM2Rb41z3xrYpm', + publicKey: + '02a06bb380cf180d703f6f80796a13555aefff817d1f6f842f1e5c555b15f0fa70', + }, + }, + + legacyAlphaTestnet: { + mnemonic: + 'apart vacuum color cream drama kind foil history hurt alone ask census', + name: 'MigrationTestAlpha on Testnet', + Path245: { + cashAddress: 'bchtest:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy539jyxazm', + fundingAddress: + 'slptest:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy5234lu2sx', + fundingWif: 'cN3NDtiabeeX1Wzg2CPgLgrb7fsDG8PgWqTnmwAhYWmY6osSta7Q', + legacyAddress: 'muCLmiGfH961UuWrTTFNU3KxH9pVQJJx6Z', + slpAddress: 'slptest:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy5234lu2sx', + }, + Path145: { + cashAddress: 'bchtest:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vtdvpkdz7', + fundingAddress: + 'slptest:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vset6v6sr', + fundingWif: 'cTKuvZ644Sf7xra5z3dPshEECJNaDyBCq7HyBsysQPh1ySSpxtQ1', + legacyAddress: 'mjWxk74mLM7TieAsSJArLF7nXqC6oc2mof', + slpAddress: 'slptest:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vset6v6sr', + }, + }, + + migratedLegacyAlphaTestnet: { + mnemonic: + 'apart vacuum color cream drama kind foil history hurt alone ask census', + name: 'MigrationTestAlpha on Testnet', + Path245: { + cashAddress: 'bchtest:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy539jyxazm', + fundingAddress: + 'slptest:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy5234lu2sx', + fundingWif: 'cN3NDtiabeeX1Wzg2CPgLgrb7fsDG8PgWqTnmwAhYWmY6osSta7Q', + legacyAddress: 'muCLmiGfH961UuWrTTFNU3KxH9pVQJJx6Z', + slpAddress: 'slptest:qztqe8k4v8ckn8cvfxt5659nhd7dcyvxy5234lu2sx', + publicKey: + '03c4a69fd90c8b196683216cffd2943a7b13b0db0812e44a4ff156ac7e03fc4ed7', + }, + Path145: { + cashAddress: 'bchtest:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vtdvpkdz7', + fundingAddress: + 'slptest:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vset6v6sr', + fundingWif: 'cTKuvZ644Sf7xra5z3dPshEECJNaDyBCq7HyBsysQPh1ySSpxtQ1', + legacyAddress: 'mjWxk74mLM7TieAsSJArLF7nXqC6oc2mof', + slpAddress: 'slptest:qq47pcxfn8n7w7jy86njd7pvgsv39l9f9vset6v6sr', + publicKey: + '02fe5308d77bcce825068a9e46adc6f032dbbe39167a7b6d05ac563ac71d8b186e', + }, + Path1899: { + cashAddress: 'bchtest:qzagy47mvh6qxkvcn3acjnz73rkhkc6y7c9ex06hrx', + fundingAddress: + 'slptest:qzagy47mvh6qxkvcn3acjnz73rkhkc6y7c7dp5qq3m', + fundingWif: 'cNRFB6MmkNhyhAj1TpGhXdbHgzWg4BsdHbAkKjiz4vt4vwgpC44F', + legacyAddress: 'mxX888y8yaPpTYh3WhrB9GEkT3cgumYwPw', + slpAddress: 'slptest:qzagy47mvh6qxkvcn3acjnz73rkhkc6y7c7dp5qq3m', + publicKey: + '02a06bb380cf180d703f6f80796a13555aefff817d1f6f842f1e5c555b15f0fa70', + }, + }, +}; diff --git a/web/cashtab-v2/src/hooks/__tests__/useWallet.test.js b/web/cashtab-v2/src/hooks/__tests__/useWallet.test.js new file mode 100644 --- /dev/null +++ b/web/cashtab-v2/src/hooks/__tests__/useWallet.test.js @@ -0,0 +1,55 @@ +/** + * @jest-environment ./config/jest/uint8array-environment + */ + +import useWallet from '../useWallet'; +import { renderHook } from '@testing-library/react-hooks'; +import mockLegacyWallets from '../__mocks__/mockLegacyWallets'; +import BCHJS from '@psf/bch-js'; +import useBCH from '../useBCH'; + +jest.mock('../useBCH'); +test('Migrating legacy wallet on testnet', async () => { + useBCH.mockReturnValue({ getBCH: () => new BCHJS() }); + const { result } = renderHook(() => useWallet()); + process = { + env: { + REACT_APP_NETWORK: `testnet`, + REACT_APP_BCHA_APIS: + 'https://rest.kingbch.com/v3/,https://wallet-service-prod.bitframe.org/v3/,notevenaurl,https://rest.kingbch.com/v3/', + REACT_APP_BCHA_APIS_TEST: 'https://free-test.fullstack.cash/v3/', + }, + }; + + const BCH = new BCHJS(); + result.current.getWallet = false; + let wallet; + wallet = await result.current.migrateLegacyWallet( + BCH, + mockLegacyWallets.legacyAlphaTestnet, + ); + + expect(wallet).toStrictEqual(mockLegacyWallets.migratedLegacyAlphaTestnet); +}); + +test('Migrating legacy wallet on mainnet', async () => { + useBCH.mockReturnValue({ getBCH: () => new BCHJS() }); + const { result } = renderHook(() => useWallet()); + process = { + env: { + REACT_APP_NETWORK: `mainnet`, + REACT_APP_BCHA_APIS: + 'https://rest.kingbch.com/v3/,https://wallet-service-prod.bitframe.org/v3/,notevenaurl,https://rest.kingbch.com/v3/', + REACT_APP_BCHA_APIS_TEST: 'https://free-test.fullstack.cash/v3/', + }, + }; + + const BCH = new BCHJS(); + result.current.getWallet = false; + let wallet; + wallet = await result.current.migrateLegacyWallet( + BCH, + mockLegacyWallets.legacyAlphaMainnet, + ); + expect(wallet).toStrictEqual(mockLegacyWallets.migratedLegacyAlphaMainnet); +});