diff --git a/libs/mocks/.babelrc b/libs/mocks/.babelrc new file mode 100644 index 000000000..1ea870ead --- /dev/null +++ b/libs/mocks/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/mocks/.eslintrc.json b/libs/mocks/.eslintrc.json new file mode 100644 index 000000000..0f2b5a124 --- /dev/null +++ b/libs/mocks/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["plugin:@nx/react", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "rules": { + "@nx/dependency-checks": [ + "error", + { + "ignoredFiles": ["libs/mocks/rollup.config.js"] + } + ] + }, + "overrides": [ + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": "error" + } + } + ] +} diff --git a/libs/mocks/README.md b/libs/mocks/README.md new file mode 100644 index 000000000..3c162cd58 --- /dev/null +++ b/libs/mocks/README.md @@ -0,0 +1,3 @@ +# mocks + +Shared mocks for testing across all libraries and apps. diff --git a/libs/mocks/jest.config.ts b/libs/mocks/jest.config.ts new file mode 100644 index 000000000..fb405bfb4 --- /dev/null +++ b/libs/mocks/jest.config.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ +export default { + displayName: 'mocks', + preset: '../../jest.preset.js', + transform: { + '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest', + '^.+\\.[tj]sx?$': [ + 'babel-jest', + { + presets: ['@nx/next/babel'], + plugins: ['@babel/plugin-transform-private-methods'], + }, + ], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/libs/mocks', +} diff --git a/libs/mocks/package.json b/libs/mocks/package.json new file mode 100644 index 000000000..45160cb94 --- /dev/null +++ b/libs/mocks/package.json @@ -0,0 +1,14 @@ +{ + "name": "@siafoundation/mocks", + "description": "Shared mocks for testing across all libraries and apps.", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@siafoundation/types": "0.1.3", + "@siafoundation/react-walletd": "3.0.0", + "@siafoundation/units": "3.0.0", + "@siafoundation/sia-central": "0.3.3", + "playwright": "^1.42.1" + }, + "types": "./src/index.d.ts" +} diff --git a/libs/mocks/project.json b/libs/mocks/project.json new file mode 100644 index 000000000..8f06bde2f --- /dev/null +++ b/libs/mocks/project.json @@ -0,0 +1,42 @@ +{ + "name": "mocks", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/mocks/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/rollup:rollup", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/mocks", + "tsConfig": "libs/mocks/tsconfig.lib.json", + "project": "libs/mocks/package.json", + "entryFile": "libs/mocks/src/index.ts", + "external": ["react/jsx-runtime"], + "compiler": "tsc", + "outputFileName": "index.js", + "rollupConfig": "libs/mocks/rollup.config.js", + "assets": [ + { + "glob": "libs/mocks/*.md", + "input": ".", + "output": "." + } + ] + }, + "configurations": {} + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/libs/mocks"], + "options": { + "jestConfig": "libs/mocks/jest.config.ts" + } + } + } +} diff --git a/libs/mocks/rollup.config.js b/libs/mocks/rollup.config.js new file mode 100644 index 000000000..3f5e4e997 --- /dev/null +++ b/libs/mocks/rollup.config.js @@ -0,0 +1,18 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const preserveDirectives = require('rollup-plugin-preserve-directives') + +// https://github.com/rollup/rollup/issues/4699#issuecomment-1465302665 +function getRollupOptions(options) { + return { + ...options, + output: { + ...options.output, + preserveModules: true, + format: 'esm', + sourcemap: true, + }, + plugins: options.plugins.concat(preserveDirectives.default()), + } +} + +module.exports = getRollupOptions diff --git a/libs/mocks/src/index.ts b/libs/mocks/src/index.ts new file mode 100644 index 000000000..40ed18fec --- /dev/null +++ b/libs/mocks/src/index.ts @@ -0,0 +1,20 @@ +export * from './siaCentral/siaCentralExchangeRates' + +export * from './walletd/consensusNetwork' +export * from './walletd/consensusTip' +export * from './walletd/consensusTipState' +export * from './walletd/defaults' +export * from './walletd/peers' +export * from './walletd/txPoolBroadcast' +export * from './walletd/wallet' +export * from './walletd/walletAddresses' +export * from './walletd/walletBalance' +export * from './walletd/walletEvents' +export * from './walletd/walletFund' +export * from './walletd/walletOutputsSiacoin' +export * from './walletd/walletOutputsSiafund' +export * from './walletd/walletRelease' +export * from './walletd/wallets' +export * from './walletd/walletTxPool' + +export * from './scenarios/seedWallet' diff --git a/libs/mocks/src/scenarios/seedWallet.ts b/libs/mocks/src/scenarios/seedWallet.ts new file mode 100644 index 000000000..1d049b05d --- /dev/null +++ b/libs/mocks/src/scenarios/seedWallet.ts @@ -0,0 +1,190 @@ +import { SiacoinElement, SiafundElement } from '@siafoundation/types' +import { + Wallet, + WalletAddress, + WalletBalanceResponse, + WalletFundResponse, +} from '@siafoundation/react-walletd' +import { toHastings } from '@siafoundation/units' +import { getMockConsensusNetworkResponse } from '../walletd/consensusNetwork' +import { getMockConsensusTipStateResponse } from '../walletd/consensusTipState' + +export function getMockScenarioSeedWallet() { + const mnemonic = + 'ridge business wish transfer home glove office salt wealth baby journey diary' + + const receiveAddress = + '5739945c21e60afd70eaf97ccd33ea27836e0219212449f39e4b38acaa8b3119aa4150a9ef0f' + const changeAddress = + '170173c40ca0f39f9618da30af14c390c7ce70248a3662a7a5d3d5a8a31c9fbfa2071e9f6518' + + const newWallet: Wallet = { + id: '100', + name: 'test send wallet', + description: 'wallet description', + dateCreated: new Date().toISOString(), + lastUpdated: new Date().toISOString(), + metadata: { + type: 'seed', + mnemonicHash: + '251cc9d01333287e9c9f39fc4749095a28a3970348a6106244848d2c414a908bc81ae4982911435a045a407fb305b69e51d05ce6f9b47ef1750c1e74ca299a48', + }, + } + + const walletBalanceResponse: WalletBalanceResponse = { + siacoins: toHastings('100').toString(), + immatureSiacoins: toHastings('10').toString(), + siafunds: 10, + } + + const walletOutputsSiacoinResponse: SiacoinElement[] = [ + { + id: 'h:aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb', + leafIndex: 304248, + merkleProof: [ + 'h:0a7a4c392f78899e3c38c5cd9e6a673b2c7afec97930af539af9c8e20209aa78', + 'h:a1e074dc48634a234b7366a0d7ab19cd05e3e698e1d44bf07e24d75ae0c65b3c', + 'h:44d107342962e2068d289ce090c87b7bf0c847f734bdfad10db5546e402c3ad7', + 'h:64ea3e65ecd5ebc1c0ce014673148060855ce550571208b6303b8e2a83e33451', + 'h:19e37bf7747c6d8c7b87d2474dbbd3a8f5b26d89642f8e1af4b9b02abdfb2ea6', + 'h:1fc2ac9f70211a3be6f334db14feb8b327458aee49d3539770640cbdec9b4a5f', + 'h:defbbc18c64349f11e75537955861ececce0fadf10baec456f6c74b024820af1', + 'h:87d27ff868ca3b1dce59ae754eaec48239718e81e2e6f3b7b418f5a00362bcf7', + 'h:93d823c55fbd09de462a8e355921433b3693d63de58ea8e2780a2c2ffabd0fee', + 'h:a68820d6b79b2735b15c69d0fc26b11252bb27f22b9088559ed13f9420f5dda1', + 'h:1bbcead690290291ea9628214a121ef783411693975171803bf5716a3a6ff19b', + ], + siacoinOutput: { + value: '1000000000000000000000000', + address: + 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', + }, + maturityHeight: 0, + }, + { + id: 'h:32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724', + leafIndex: 305723, + merkleProof: [ + 'h:8c02aeec48de589ce497ebe72fb8b527cfe022ef513fcfdc56745c84832f00ec', + 'h:1bf63b9959e60272fd7a48a8cecd4120a852c0e14557ea27ccad6ea2071e70b3', + 'h:21b7e1606e9fd677059c58a1a687682182f71ce09e071431dcaff823a3a5d49e', + 'h:e81ac37d3b4db6166dc1bb10ebfa49f57cbf99aababd36ee4e3e5e12082dc6dc', + 'h:ecc307c6c3e505d97ccf821938e5e5702ef0130d33c991ca95735f7d9706a4b8', + 'h:9560060ee399793f102e092afdfdbdd33692706256955e8390af552de0addfc0', + ], + siacoinOutput: { + value: '97988210000000000000000000', + address: + 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', + }, + maturityHeight: 0, + }, + ] + + const walletOutputsSiafundResponse: SiafundElement[] = [] + + const walletFundResponse: WalletFundResponse = { + transaction: { + siacoinInputs: [ + { + parentID: + 'scoid:aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb', + unlockConditions: { + timelock: 0, + publicKeys: null, + signaturesRequired: 1, + }, + }, + { + parentID: + 'scoid:32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724', + unlockConditions: { + timelock: 0, + publicKeys: null, + signaturesRequired: 1, + }, + }, + ], + siacoinOutputs: [ + { + value: '1000000000000000000000000', + address: + 'addr:90c6057cdd2463eca61f83796e83152dbba28b6cb9a74831a043833051ec9f422726bfff2ee8', + }, + { + value: '97984280000000000000000000', + address: + 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', + }, + ], + minerFees: ['3930000000000000000000'], + }, + toSign: [ + 'h:aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb', + 'h:32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724', + ], + dependsOn: null, + } + + const walletAddressesResponse: WalletAddress[] = [ + { + address: + 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', + description: '', + metadata: { + index: 0, + unlockConditions: { + timelock: 0, + signaturesRequired: 1, + publicKeys: [ + 'ed25519:ee122b2169bdae5776b55609e384e0c58372cd5c529d4edc9b9918b26f8e5535', + ], + }, + }, + }, + { + address: + 'addr:90c6057cdd2463eca61f83796e83152dbba28b6cb9a74831a043833051ec9f422726bfff2ee8', + description: '', + metadata: { + index: 1, + unlockConditions: { + timelock: 0, + signaturesRequired: 1, + publicKeys: [ + 'ed25519:624d6d477a8f4ceac873e6dd9138740f9322cb34a24246f96f9d64c021172f43', + ], + }, + }, + }, + { + address: + 'addr:170173c40ca0f39f9618da30af14c390c7ce70248a3662a7a5d3d5a8a31c9fbfa2071e9f6518', + description: '', + metadata: { + index: 2, + unlockConditions: { + timelock: 0, + signaturesRequired: 1, + publicKeys: [ + 'ed25519:65cac661a4acf36847c0aa67cbc6956e3449fd82a7430cfd673ea7fedbfcf5fa', + ], + }, + }, + }, + ] + + return { + consensusState: getMockConsensusTipStateResponse(), + consensusNetwork: getMockConsensusNetworkResponse(), + mnemonic, + receiveAddress, + changeAddress, + newWallet, + walletBalanceResponse, + walletOutputsSiacoinResponse, + walletOutputsSiafundResponse, + walletFundResponse, + walletAddressesResponse, + } +} diff --git a/libs/mocks/src/siaCentral/siaCentralExchangeRates.ts b/libs/mocks/src/siaCentral/siaCentralExchangeRates.ts new file mode 100644 index 000000000..dda0d9be9 --- /dev/null +++ b/libs/mocks/src/siaCentral/siaCentralExchangeRates.ts @@ -0,0 +1,39 @@ +import { SiaCentralExchangeRatesResponse } from '@siafoundation/sia-central' +import { Page } from 'playwright' + +export function getMockSiaCentralExchangeRatesResponse(): SiaCentralExchangeRatesResponse { + return { + message: 'successfully retrieved exchange rate', + type: 'success', + rates: { + sc: { + aud: '0.016136871549', + bch: '0.000021499880703', + btc: '0.000000149047', + cad: '0.014328484298', + cny: '0.076310722577', + eth: '0.0000029068532077', + eur: '0.009737538604', + gbp: '0.008359151948', + jpy: '1.600530478116', + ltc: '0.000116295710314', + rub: '0.978819669836', + scp: '0.0623627615062762', + sf: '0.000000745235', + usd: '0.010571307522', + }, + }, + timestamp: '2024-03-26T13:12:22.7348119Z', + } +} + +export async function mockApiSiaCentralExchangeRates({ page }: { page: Page }) { + const json = getMockSiaCentralExchangeRatesResponse() + await page.route( + 'https://api.siacentral.com/v2/market/exchange-rate?currencies=sc', + async (route) => { + await route.fulfill({ json }) + } + ) + return json +} diff --git a/libs/mocks/src/walletd/consensusNetwork.ts b/libs/mocks/src/walletd/consensusNetwork.ts new file mode 100644 index 000000000..66dec3d50 --- /dev/null +++ b/libs/mocks/src/walletd/consensusNetwork.ts @@ -0,0 +1,55 @@ +import { ConsensusNetwork } from '@siafoundation/types' +import { Page } from 'playwright' + +export function getMockConsensusNetworkResponse(): ConsensusNetwork { + return { + name: 'zen', + initialCoinbase: '300000000000000000000000000000', + minimumCoinbase: '300000000000000000000000000000', + initialTarget: + 'bid:0000000100000000000000000000000000000000000000000000000000000000', + hardforkDevAddr: { + height: 1, + oldAddress: + 'addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69', + newAddress: + 'addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69', + }, + hardforkTax: { + height: 2, + }, + hardforkStorageProof: { + height: 5, + }, + hardforkOak: { + height: 10, + fixHeight: 12, + genesisTimestamp: '2023-01-13T03:53:20-05:00', + }, + hardforkASIC: { + height: 20, + oakTime: 10000000000000, + oakTarget: + 'bid:0000000100000000000000000000000000000000000000000000000000000000', + }, + hardforkFoundation: { + height: 30, + primaryAddress: + 'addr:053b2def3cbdd078c19d62ce2b4f0b1a3c5e0ffbeeff01280efb1f8969b2f5bb4fdc680f0807', + failsafeAddress: + 'addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69', + }, + hardforkV2: { + allowHeight: 100000, + requireHeight: 102000, + }, + } +} + +export async function mockApiConsensusNetwork({ page }: { page: Page }) { + const json = getMockConsensusNetworkResponse() + await page.route('**/api/consensus/network*', async (route) => { + await route.fulfill({ json }) + }) + return json +} diff --git a/libs/mocks/src/walletd/consensusTip.ts b/libs/mocks/src/walletd/consensusTip.ts new file mode 100644 index 000000000..b829d9f17 --- /dev/null +++ b/libs/mocks/src/walletd/consensusTip.ts @@ -0,0 +1,17 @@ +import { ConsensusTipResponse } from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export function getMockConsensusTipResponse(): ConsensusTipResponse { + return { + height: 61676, + id: 'bid:00000010d5da9002b9640d920d9eb9f7502c5c3b2a796ecf800a103920bea96f', + } +} + +export async function mockApiConsensusTipState({ page }: { page: Page }) { + const json = getMockConsensusTipResponse() + await page.route('**/api/consensus/tipstate*', async (route) => { + await route.fulfill({ json }) + }) + return json +} diff --git a/libs/mocks/src/walletd/consensusTipState.ts b/libs/mocks/src/walletd/consensusTipState.ts new file mode 100644 index 000000000..414267a64 --- /dev/null +++ b/libs/mocks/src/walletd/consensusTipState.ts @@ -0,0 +1,65 @@ +import { ConsensusState } from '@siafoundation/types' +import { Page } from 'playwright' + +export function getMockConsensusTipStateResponse(): ConsensusState { + return { + index: { + height: 61676, + id: 'bid:00000010d5da9002b9640d920d9eb9f7502c5c3b2a796ecf800a103920bea96f', + }, + prevTimestamps: [ + '2024-03-25T17:33:24-04:00', + '2024-03-25T17:18:32-04:00', + '2024-03-25T17:07:59-04:00', + '2024-03-25T17:04:15-04:00', + '2024-03-25T16:50:56-04:00', + '2024-03-25T16:38:22-04:00', + '2024-03-25T16:38:12-04:00', + '2024-03-25T16:36:56-04:00', + '2024-03-25T16:35:17-04:00', + '2024-03-25T16:33:59-04:00', + '2024-03-25T16:31:16-04:00', + ], + depth: + 'bid:000000000001aa096ddebfbf467c56faba46eb4b3e53e72c775bf9ab202a4890', + childTarget: + 'bid:0000005a6c49bca6186415827e15b13371c3de541dd64b7cf36a519b5128b2f8', + siafundPool: '88386728360671853873752300000', + oakTime: 127103000000000, + oakTarget: + 'bid:0000000024d11bd550a6acf3718192825fa2cef5582314db8e92c4f4e5efbf4c', + foundationPrimaryAddress: + 'addr:053b2def3cbdd078c19d62ce2b4f0b1a3c5e0ffbeeff01280efb1f8969b2f5bb4fdc680f0807', + foundationFailsafeAddress: + 'addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69', + totalWork: '169134658088983', + difficulty: '47498615', + oakWork: '29864374508', + elements: { + numLeaves: 304383, + trees: [ + 'h:6d5fedd2f6d71cad5fc7bb35ef8bad6631c63e1323142e2a4825314b77f87d1e', + 'h:16e0c70194d2e41a21fe070ff936551b2379df8054890b95bada3c6b6a0ed3f7', + 'h:73defbf6af352d9401698492c407662b8feb62ba82e92028bf9bf0103f261844', + 'h:e22d4da6204096eef1eb0fc217c8900dce1040312cc721b9b6b3fc2be7a77da8', + 'h:4a9860d3e3ccf6cf434ed94d485b4385c62c443fd18e539998bd817a3ebed772', + 'h:6cd1e1d71a0796980193bcbb1adff069ef3f70fd12749d9e877b2bd0c8d561d1', + 'h:7aa0bdb94da13c43abd9446f77638d3e6e2ae4c36c5224a76433044c56db906c', + 'h:66c119cf5cb9ca3a174637c751988f03300e0375b43d22c82df7145841515d38', + 'h:ce8a94296e60887232915d6801a9246827661d329de47a08f25a6a3884809037', + 'h:8aa7872a63ef37e1ca8e4211700daf96068ce2cc215d4e2ab45434b5ffcab818', + 'h:3f0175c94ed31190e6da23e80d6167e2da619b460e27798750491163dbe5677b', + 'h:9f120f6489c3e3a324cd51a6f8f9dd033930688095f4440dc5f8511692b4efc1', + ], + }, + attestations: 0, + } +} + +export async function mockApiConsensusTip({ page }: { page: Page }) { + const json = getMockConsensusTipStateResponse() + await page.route('**/api/consensus/tip*', async (route) => { + await route.fulfill({ json }) + }) + return json +} diff --git a/libs/mocks/src/walletd/defaults.ts b/libs/mocks/src/walletd/defaults.ts new file mode 100644 index 000000000..2667180df --- /dev/null +++ b/libs/mocks/src/walletd/defaults.ts @@ -0,0 +1,22 @@ +import { Page } from 'playwright' +import { mockApiSiaCentralExchangeRates } from '../siaCentral/siaCentralExchangeRates' +import { mockApiSyncerPeers } from './peers' +import { mockApiConsensusTip } from './consensusTipState' +import { mockApiConsensusTipState } from './consensusTip' +import { mockApiConsensusNetwork } from './consensusNetwork' +import { mockApiWallets } from './wallets' +import { mockApiTxPoolBroadcast } from './txPoolBroadcast' +import { mockApiWallet } from './wallet' + +export async function mockApiDefaults({ page }: { page: Page }) { + await mockApiSiaCentralExchangeRates({ page }) + await mockApiSyncerPeers({ page }) + await mockApiConsensusTip({ page }) + await mockApiConsensusTipState({ page }) + await mockApiConsensusNetwork({ page }) + await mockApiTxPoolBroadcast({ page }) + const wallets = await mockApiWallets({ page }) + for (const wallet of wallets) { + await mockApiWallet({ page, wallet }) + } +} diff --git a/libs/mocks/src/walletd/peers.ts b/libs/mocks/src/walletd/peers.ts new file mode 100644 index 000000000..f6a0511b9 --- /dev/null +++ b/libs/mocks/src/walletd/peers.ts @@ -0,0 +1,83 @@ +import { SyncerPeersResponse } from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export function getMockPeersResponse(): SyncerPeersResponse { + return [ + { + addr: '51.81.242.140:9881', + inbound: false, + version: '1.5.4', + firstSeen: '2024-03-20T17:14:43Z', + connectedSince: '2024-03-25T21:08:49Z', + syncDuration: 1321006459, + }, + { + addr: '3.36.68.121:9881', + inbound: false, + version: '1.5.4', + firstSeen: '2024-03-20T13:39:10Z', + connectedSince: '2024-03-25T21:08:50Z', + syncDuration: 4230014834, + }, + { + addr: '185.200.116.131:9807', + inbound: false, + version: '1.5.4', + firstSeen: '2024-03-20T17:14:43Z', + connectedSince: '2024-03-25T21:08:51Z', + syncedBlocks: 2, + syncDuration: 6018991169, + }, + { + addr: '62.30.63.90:9881', + inbound: false, + version: '1.5.4', + firstSeen: '2024-03-20T13:39:10Z', + connectedSince: '2024-03-25T21:09:07Z', + syncDuration: 6341708209, + }, + { + addr: '43.203.121.70:9881', + inbound: false, + version: '1.5.4', + firstSeen: '2024-03-20T17:14:44Z', + connectedSince: '2024-03-25T21:09:08Z', + syncedBlocks: 3, + syncDuration: 6860545337, + }, + { + addr: '23.239.8.40:9881', + inbound: false, + version: '1.5.4', + firstSeen: '2024-03-20T13:39:10Z', + connectedSince: '2024-03-25T21:09:08Z', + syncDuration: 580416082, + }, + { + addr: '141.94.161.198:9881', + inbound: false, + version: '1.5.4', + firstSeen: '2024-03-20T13:39:10Z', + connectedSince: '2024-03-25T21:09:13Z', + syncedBlocks: 93, + syncDuration: 4894896166, + }, + { + addr: '64.227.180.244:9881', + inbound: false, + version: '1.5.4', + firstSeen: '2024-03-20T13:39:25Z', + connectedSince: '2024-03-25T21:08:49Z', + syncedBlocks: 2, + syncDuration: 4687705000, + }, + ] +} + +export async function mockApiSyncerPeers({ page }: { page: Page }) { + const json = getMockPeersResponse() + await page.route('**/api/syncer/peers*', async (route) => { + await route.fulfill({ json }) + }) + return json +} diff --git a/libs/mocks/src/walletd/txPoolBroadcast.ts b/libs/mocks/src/walletd/txPoolBroadcast.ts new file mode 100644 index 000000000..f514346a0 --- /dev/null +++ b/libs/mocks/src/walletd/txPoolBroadcast.ts @@ -0,0 +1,7 @@ +import { Page } from 'playwright' + +export async function mockApiTxPoolBroadcast({ page }: { page: Page }) { + await page.route(`**/api/txpool/broadcast*`, async (route) => { + await route.fulfill() + }) +} diff --git a/libs/mocks/src/walletd/wallet.ts b/libs/mocks/src/walletd/wallet.ts new file mode 100644 index 000000000..d0105f884 --- /dev/null +++ b/libs/mocks/src/walletd/wallet.ts @@ -0,0 +1,59 @@ +import { Page } from 'playwright' +import { + Wallet, + WalletAddressesResponse, + WalletBalanceResponse, + WalletFundResponse, + WalletOutputsSiacoinResponse, +} from '@siafoundation/react-walletd' +import { mockApiWalletBalance } from './walletBalance' +import { mockApiWalletAddresses } from './walletAddresses' +import { mockApiWalletEvents } from './walletEvents' +import { mockApiWalletTxPool } from './walletTxPool' +import { mockApiWalletOutputsSiacoin } from './walletOutputsSiacoin' +import { mockApiWalletOutputsSiafund } from './walletOutputsSiafund' +import { mockApiWalletFund } from './walletFund' +import { mockApiWalletRelease } from './walletRelease' + +export async function mockApiWallet({ + page, + wallet, + responses = {}, +}: { + page: Page + wallet: Wallet + responses?: { + balance?: WalletBalanceResponse + outputsSiacoin?: WalletOutputsSiacoinResponse + fund?: WalletFundResponse + addresses?: WalletAddressesResponse + } +}) { + await mockApiWalletBalance({ + page, + walletId: wallet.id, + response: responses.balance, + }) + await mockApiWalletAddresses({ + page, + walletId: wallet.id, + response: responses.addresses, + }) + await mockApiWalletEvents({ page, walletId: wallet.id }) + await mockApiWalletTxPool({ page, walletId: wallet.id }) + await mockApiWalletOutputsSiacoin({ + page, + walletId: wallet.id, + response: responses.outputsSiacoin, + }) + await mockApiWalletOutputsSiafund({ page, walletId: wallet.id }) + await mockApiWalletFund({ + page, + walletId: wallet.id, + response: responses.fund, + }) + await mockApiWalletRelease({ + page, + walletId: wallet.id, + }) +} diff --git a/libs/mocks/src/walletd/walletAddresses.ts b/libs/mocks/src/walletd/walletAddresses.ts new file mode 100644 index 000000000..7a3b246a2 --- /dev/null +++ b/libs/mocks/src/walletd/walletAddresses.ts @@ -0,0 +1,79 @@ +import { + WalletAddress, + WalletAddressesResponse, +} from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export function getMockWalletAddressesResponse(): WalletAddressesResponse { + return [ + { + address: + 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', + description: '', + metadata: { + index: 0, + unlockConditions: { + signaturesRequired: 1, + timelock: 0, + publicKeys: [ + 'ed25519:ee122b2169bdae5776b55609e384e0c58372cd5c529d4edc9b9918b26f8e5535', + ], + }, + }, + }, + { + address: + 'addr:90c6057cdd2463eca61f83796e83152dbba28b6cb9a74831a043833051ec9f422726bfff2ee8', + description: '', + metadata: { + index: 1, + unlockConditions: { + signaturesRequired: 1, + timelock: 0, + publicKeys: [ + 'ed25519:624d6d477a8f4ceac873e6dd9138740f9322cb34a24246f96f9d64c021172f43', + ], + }, + }, + }, + { + address: + 'addr:170173c40ca0f39f9618da30af14c390c7ce70248a3662a7a5d3d5a8a31c9fbfa2071e9f6518', + description: '', + metadata: { + index: 2, + unlockConditions: { + signaturesRequired: 1, + timelock: 0, + publicKeys: [ + 'ed25519:65cac661a4acf36847c0aa67cbc6956e3449fd82a7430cfd673ea7fedbfcf5fa', + ], + }, + }, + }, + ] +} + +export async function mockApiWalletAddresses({ + page, + walletId, + response, +}: { + page: Page + walletId: string + response?: WalletAddress[] +}) { + const json = response || getMockWalletAddressesResponse() + await page.route(`**/api/wallets/${walletId}/addresses*`, async (route) => { + if (route.request().method() === 'PUT') { + const data = route.request().postData() + if (data) { + json.push(JSON.parse(data)) + } + await route.fulfill({ json }) + } else { + await route.fulfill({ json }) + } + }) + return json +} diff --git a/libs/mocks/src/walletd/walletBalance.ts b/libs/mocks/src/walletd/walletBalance.ts new file mode 100644 index 000000000..211502648 --- /dev/null +++ b/libs/mocks/src/walletd/walletBalance.ts @@ -0,0 +1,27 @@ +import { WalletBalanceResponse } from '@siafoundation/react-walletd' +import { toHastings } from '@siafoundation/units' +import { Page } from 'playwright' + +export function getMockWalletBalanceResponse(): WalletBalanceResponse { + return { + siacoins: toHastings('100').toString(), + immatureSiacoins: toHastings('10').toString(), + siafunds: 10, + } +} + +export async function mockApiWalletBalance({ + page, + walletId, + response, +}: { + page: Page + walletId: string + response?: WalletBalanceResponse +}) { + const json = response || getMockWalletBalanceResponse() + await page.route(`**/api/wallets/${walletId}/balance*`, async (route) => { + await route.fulfill({ json }) + }) + return json +} diff --git a/libs/mocks/src/walletd/walletEvents.ts b/libs/mocks/src/walletd/walletEvents.ts new file mode 100644 index 000000000..8c2ea854b --- /dev/null +++ b/libs/mocks/src/walletd/walletEvents.ts @@ -0,0 +1,20 @@ +import { WalletEventsResponse } from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export function getMockWalletEventsResponse(): WalletEventsResponse { + return [] +} + +export async function mockApiWalletEvents({ + page, + walletId, +}: { + page: Page + walletId: string +}) { + const json = getMockWalletEventsResponse() + await page.route(`**/api/wallets/${walletId}/events*`, async (route) => { + await route.fulfill({ json }) + }) + return json +} diff --git a/libs/mocks/src/walletd/walletFund.ts b/libs/mocks/src/walletd/walletFund.ts new file mode 100644 index 000000000..8117eeed5 --- /dev/null +++ b/libs/mocks/src/walletd/walletFund.ts @@ -0,0 +1,63 @@ +import { WalletFundResponse } from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export function getMockWalletFundResponse(): WalletFundResponse { + return { + transaction: { + siacoinInputs: [ + { + parentID: + 'scoid:aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb', + unlockConditions: { + timelock: 0, + publicKeys: null, + signaturesRequired: 0, + }, + }, + { + parentID: + 'scoid:32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724', + unlockConditions: { + timelock: 0, + publicKeys: null, + signaturesRequired: 0, + }, + }, + ], + siacoinOutputs: [ + { + value: '1000000000000000000000000', + address: + 'addr:90c6057cdd2463eca61f83796e83152dbba28b6cb9a74831a043833051ec9f422726bfff2ee8', + }, + { + value: '97984280000000000000000000', + address: + 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', + }, + ], + minerFees: ['3930000000000000000000'], + }, + toSign: [ + 'h:aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb', + 'h:32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724', + ], + dependsOn: null, + } +} + +export async function mockApiWalletFund({ + page, + walletId, + response, +}: { + page: Page + walletId: string + response?: WalletFundResponse +}) { + const json = response || getMockWalletFundResponse() + await page.route(`**/api/wallets/${walletId}/fund*`, async (route) => { + await route.fulfill({ json }) + }) + return json +} diff --git a/libs/mocks/src/walletd/walletOutputsSiacoin.ts b/libs/mocks/src/walletd/walletOutputsSiacoin.ts new file mode 100644 index 000000000..d00905147 --- /dev/null +++ b/libs/mocks/src/walletd/walletOutputsSiacoin.ts @@ -0,0 +1,68 @@ +import { WalletOutputsSiacoinResponse } from '@siafoundation/react-walletd' +import { SiacoinElement } from '@siafoundation/types' +import { Page } from 'playwright' + +export function getMockWalletOutputsSiacoinResponse(): WalletOutputsSiacoinResponse { + return [ + { + id: 'h:aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb', + leafIndex: 304248, + merkleProof: [ + 'h:0a7a4c392f78899e3c38c5cd9e6a673b2c7afec97930af539af9c8e20209aa78', + 'h:a1e074dc48634a234b7366a0d7ab19cd05e3e698e1d44bf07e24d75ae0c65b3c', + 'h:44d107342962e2068d289ce090c87b7bf0c847f734bdfad10db5546e402c3ad7', + 'h:64ea3e65ecd5ebc1c0ce014673148060855ce550571208b6303b8e2a83e33451', + 'h:19e37bf7747c6d8c7b87d2474dbbd3a8f5b26d89642f8e1af4b9b02abdfb2ea6', + 'h:1fc2ac9f70211a3be6f334db14feb8b327458aee49d3539770640cbdec9b4a5f', + 'h:defbbc18c64349f11e75537955861ececce0fadf10baec456f6c74b024820af1', + 'h:87d27ff868ca3b1dce59ae754eaec48239718e81e2e6f3b7b418f5a00362bcf7', + 'h:93d823c55fbd09de462a8e355921433b3693d63de58ea8e2780a2c2ffabd0fee', + 'h:a68820d6b79b2735b15c69d0fc26b11252bb27f22b9088559ed13f9420f5dda1', + 'h:1bbcead690290291ea9628214a121ef783411693975171803bf5716a3a6ff19b', + ], + siacoinOutput: { + value: '1000000000000000000000000', + address: + 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', + }, + maturityHeight: 0, + }, + { + id: 'h:32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724', + leafIndex: 305723, + merkleProof: [ + 'h:8c02aeec48de589ce497ebe72fb8b527cfe022ef513fcfdc56745c84832f00ec', + 'h:1bf63b9959e60272fd7a48a8cecd4120a852c0e14557ea27ccad6ea2071e70b3', + 'h:21b7e1606e9fd677059c58a1a687682182f71ce09e071431dcaff823a3a5d49e', + 'h:e81ac37d3b4db6166dc1bb10ebfa49f57cbf99aababd36ee4e3e5e12082dc6dc', + 'h:ecc307c6c3e505d97ccf821938e5e5702ef0130d33c991ca95735f7d9706a4b8', + 'h:9560060ee399793f102e092afdfdbdd33692706256955e8390af552de0addfc0', + ], + siacoinOutput: { + value: '97988210000000000000000000', + address: + 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', + }, + maturityHeight: 0, + }, + ] +} + +export async function mockApiWalletOutputsSiacoin({ + page, + walletId, + response, +}: { + page: Page + walletId: string + response?: SiacoinElement[] +}) { + const json = response || getMockWalletOutputsSiacoinResponse() + await page.route( + `**/api/wallets/${walletId}/outputs/siacoin*`, + async (route) => { + await route.fulfill({ json }) + } + ) + return json +} diff --git a/libs/mocks/src/walletd/walletOutputsSiafund.ts b/libs/mocks/src/walletd/walletOutputsSiafund.ts new file mode 100644 index 000000000..3b11c3e4b --- /dev/null +++ b/libs/mocks/src/walletd/walletOutputsSiafund.ts @@ -0,0 +1,23 @@ +import { WalletOutputsSiafundResponse } from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export function getMockWalletOutputsSiafundResponse(): WalletOutputsSiafundResponse { + return [] +} + +export async function mockApiWalletOutputsSiafund({ + page, + walletId, +}: { + page: Page + walletId: string +}) { + const json = getMockWalletOutputsSiafundResponse() + await page.route( + `**/api/wallets/${walletId}/outputs/siafund*`, + async (route) => { + await route.fulfill({ json }) + } + ) + return json +} diff --git a/libs/mocks/src/walletd/walletRelease.ts b/libs/mocks/src/walletd/walletRelease.ts new file mode 100644 index 000000000..a47a12c1a --- /dev/null +++ b/libs/mocks/src/walletd/walletRelease.ts @@ -0,0 +1,15 @@ +import { WalletFundResponse } from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export async function mockApiWalletRelease({ + page, + walletId, +}: { + page: Page + walletId: string + response?: WalletFundResponse +}) { + await page.route(`**/api/wallets/${walletId}/release*`, async (route) => { + await route.fulfill() + }) +} diff --git a/libs/mocks/src/walletd/walletTxPool.ts b/libs/mocks/src/walletd/walletTxPool.ts new file mode 100644 index 000000000..1b019fe97 --- /dev/null +++ b/libs/mocks/src/walletd/walletTxPool.ts @@ -0,0 +1,20 @@ +import { WalletTxPoolResponse } from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export function getMockWalletTxPoolResponse(): WalletTxPoolResponse { + return [] +} + +export async function mockApiWalletTxPool({ + page, + walletId, +}: { + page: Page + walletId: string +}) { + const json = getMockWalletTxPoolResponse() + await page.route(`**/api/wallets/${walletId}/txpool*`, async (route) => { + await route.fulfill({ json }) + }) + return json +} diff --git a/libs/mocks/src/walletd/wallets.ts b/libs/mocks/src/walletd/wallets.ts new file mode 100644 index 000000000..6b791d788 --- /dev/null +++ b/libs/mocks/src/walletd/wallets.ts @@ -0,0 +1,59 @@ +import { Wallet, WalletsResponse } from '@siafoundation/react-walletd' +import { Page } from 'playwright' + +export function getMockWalletsResponse(): WalletsResponse { + return [ + { + id: '1', + name: '1', + description: '1', + dateCreated: '2024-03-20T18:49:01Z', + lastUpdated: '2024-03-20T18:49:01Z', + metadata: { + type: 'seed', + mnemonicHash: + '302626ee851d3712c19007c6141fc7f3a229ae64e6df71403a31b3ad131ac53657cf3dcae9405d9203e57204eb0084a7899224880e8d8beb4e103949255d861e', + }, + }, + { + id: '2', + name: '2', + description: '2', + dateCreated: '2024-03-20T18:49:50Z', + lastUpdated: '2024-03-20T19:30:04Z', + metadata: { + type: 'seed', + mnemonicHash: + '302626ee851d3712c19007c6141fc7f3a229ae64e6df71403a31b3ad131ac53657cf3dcae9405d9203e57204eb0084a7899224880e8d8beb4e103949255d861e', + }, + }, + { + id: '3', + name: '3', + description: '3', + dateCreated: '2024-03-20T18:53:27Z', + lastUpdated: '2024-03-20T18:53:27Z', + metadata: { + type: 'watch', + }, + }, + ] +} + +export async function mockApiWallets({ + page, + createWallet, +}: { + page: Page + createWallet?: Wallet +}) { + const json = getMockWalletsResponse() + await page.route('**/api/wallets*', async (route) => { + if (route.request().method() === 'POST') { + await route.fulfill({ json: createWallet }) + } else { + await route.fulfill({ json }) + } + }) + return json +} diff --git a/libs/mocks/tsconfig.json b/libs/mocks/tsconfig.json new file mode 100644 index 000000000..4c089585e --- /dev/null +++ b/libs/mocks/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/mocks/tsconfig.lib.json b/libs/mocks/tsconfig.lib.json new file mode 100644 index 000000000..d73537814 --- /dev/null +++ b/libs/mocks/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts" + ] + }, + "exclude": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/libs/mocks/tsconfig.spec.json b/libs/mocks/tsconfig.spec.json new file mode 100644 index 000000000..503e9a83d --- /dev/null +++ b/libs/mocks/tsconfig.spec.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/libs/types/src/core.ts b/libs/types/src/core.ts index 3a297a5bb..84f22cea4 100644 --- a/libs/types/src/core.ts +++ b/libs/types/src/core.ts @@ -22,7 +22,7 @@ export type StateElement = { export type UnlockConditions = { timelock: number - publicKeys?: PublicKey[] + publicKeys?: PublicKey[] | null signaturesRequired: number } diff --git a/tsconfig.base.json b/tsconfig.base.json index c944ddae6..997140b78 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -32,7 +32,8 @@ "@siafoundation/sdk": ["dist/libs/sdk/index.esm.js"], "@siafoundation/sia-central": ["libs/sia-central/src/index.ts"], "@siafoundation/types": ["libs/types/src/index.ts"], - "@siafoundation/units": ["libs/units/src/index.ts"] + "@siafoundation/units": ["libs/units/src/index.ts"], + "@siafoundation/mocks": ["libs/mocks/src/index.ts"] } }, "exclude": ["node_modules", "tmp"]