From a1989a950b794af253a57897f97d02264cd7b25a Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Thu, 5 Dec 2024 15:51:10 -0800 Subject: [PATCH] Create our own testing framework. Needs some double-checking on accuracy. --- favicon.ico | Bin 0 -> 176148 bytes jest.config.ts | 199 +++++++ package-lock.json | 788 ++++++++++++++++++++++++++++ package.json | 6 +- src/lib/rpc.ts | 3 +- src/lib/wallet.ts | 32 +- test.html | 6 +- test/GLOBALS.mjs | 74 ++- test/create-wallet.test.mjs | 93 ++-- test/derive-accounts.test.mjs | 71 ++- test/import-wallet.test.mjs | 157 +++--- test/lock-unlock-wallet.mjs | 73 ++- test/main.mjs | 16 + test/manage-rolodex.mjs | 69 ++- {perf => test/perf}/account.perf.js | 14 +- {perf => test/perf}/wallet.perf.js | 13 +- test/refresh-accounts.test.mjs | 69 +-- test/sign-blocks.test.mjs | 45 +- test/tools.test.mjs | 55 +- 19 files changed, 1426 insertions(+), 357 deletions(-) create mode 100644 favicon.ico create mode 100644 jest.config.ts create mode 100644 test/main.mjs rename {perf => test/perf}/account.perf.js (67%) rename {perf => test/perf}/wallet.perf.js (62%) diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..024ed21b4c597e4cbb816f020d7c4db61a24bb7c GIT binary patch literal 176148 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" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "jsdom", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + testMatch: [ + "**/__tests__/**/*.m[jt]s?(x)", + "**/?(*.)+(spec|test).m[tj]s?(x)" + ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // A map from regular expressions to paths to transformers + transform: {}, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +} + +export default config diff --git a/package-lock.json b/package-lock.json index 4bd52d2..149f0bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,8 @@ "@types/web-bluetooth": "^0.0.20", "esbuild": "^0.24.0", "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "ts-node": "^10.9.2", "typescript": "^5.6.3" }, "funding": { @@ -548,6 +550,30 @@ "dev": true, "license": "MIT" }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.24.0", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", @@ -1044,6 +1070,44 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1126,6 +1190,18 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "node_modules/@types/node": { "version": "22.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", @@ -1143,6 +1219,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/w3c-web-hid": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/w3c-web-hid/-/w3c-web-hid-1.0.6.tgz", @@ -1181,6 +1264,64 @@ "dev": true, "license": "MIT" }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1237,6 +1378,13 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1247,6 +1395,13 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -1598,6 +1753,19 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1634,6 +1802,13 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1649,6 +1824,48 @@ "node": ">= 8" } }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -1667,6 +1884,13 @@ } } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true, + "license": "MIT" + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -1692,6 +1916,16 @@ "node": ">=0.10.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -1702,6 +1936,16 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -1712,6 +1956,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.70", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.70.tgz", @@ -1739,6 +1997,19 @@ "dev": true, "license": "MIT" }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1809,6 +2080,28 @@ "node": ">=8" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1823,6 +2116,26 @@ "node": ">=4" } }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -1927,6 +2240,21 @@ "node": ">=8" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2064,6 +2392,19 @@ "node": ">= 0.4" } }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -2071,6 +2412,35 @@ "dev": true, "license": "MIT" }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -2081,6 +2451,19 @@ "node": ">=10.17.0" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -2183,6 +2566,13 @@ "node": ">=0.12.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2474,6 +2864,34 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", @@ -2878,6 +3296,52 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", @@ -2977,6 +3441,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -3008,6 +3479,29 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -3082,6 +3576,13 @@ "node": ">=8" } }, + "node_modules/nwsapi": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3182,6 +3683,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3304,6 +3818,29 @@ "node": ">= 6" } }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -3321,6 +3858,13 @@ ], "license": "MIT" }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -3338,6 +3882,13 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -3399,6 +3950,26 @@ "tslib": "^2.1.0" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -3601,6 +4172,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -3636,6 +4214,79 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -3687,6 +4338,16 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -3718,6 +4379,24 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -3733,6 +4412,19 @@ "node": ">=10.12.0" } }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -3743,6 +4435,53 @@ "makeerror": "1.0.12" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3798,6 +4537,45 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -3844,6 +4622,16 @@ "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index f54178a..106a743 100644 --- a/package.json +++ b/package.json @@ -43,11 +43,11 @@ }, "scripts": { "build": "rm -rf dist && tsc && esbuild main.min=dist/main.js global.min=dist/global.js --outdir=dist --target=esnext --format=esm --platform=browser --bundle --sourcemap", - "test": "npm run build && jest", + "test": "npm run build && esbuild test.min=test/main.mjs --outdir=dist --target=esnext --format=esm --platform=browser --bundle --sourcemap", "test:node": "npm run build -- --platform=node && node --test --test-force-exit --env-file .env", "test:coverage": "npm run test:node -- --experimental-test-coverage", "test:coverage:report": "npm run test:coverage -- --test-reporter=lcov --test-reporter-destination=coverage.info && genhtml coverage.info --output-directory test/coverage && rm coverage.info && xdg-open test/coverage/index.html", - "test:performance": "npm run test perf/*" + "test:performance": "npm run build && npm run jest -- ./perf/*" }, "imports": { "#*": "./*" @@ -65,6 +65,8 @@ "@types/web-bluetooth": "^0.0.20", "esbuild": "^0.24.0", "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "ts-node": "^10.9.2", "typescript": "^5.6.3" }, "type": "module", diff --git a/src/lib/rpc.ts b/src/lib/rpc.ts index 0e28d59..d43f237 100644 --- a/src/lib/rpc.ts +++ b/src/lib/rpc.ts @@ -25,10 +25,11 @@ export class Rpc { * @returns {Promise} JSON-formatted RPC results from the node */ async call (action: string, data?: { [key: string]: any }): Promise { + var process: any = process || null this.#validate(action) const headers: { [key: string]: string } = {} headers['Content-Type'] = 'application/json' - if (this.#n && process.env.LIBNEMO_RPC_API_KEY) { + if (this.#n && process?.env?.LIBNEMO_RPC_API_KEY) { headers[this.#n] = process.env.LIBNEMO_RPC_API_KEY } diff --git a/src/lib/wallet.ts b/src/lib/wallet.ts index 142f2b9..2c3ae3b 100644 --- a/src/lib/wallet.ts +++ b/src/lib/wallet.ts @@ -272,9 +272,9 @@ export class Bip44Wallet extends Wallet { if (!Bip44Wallet.#isInternal) { throw new Error(`Bip44Wallet cannot be instantiated directly. Use 'await Bip44Wallet.create()' instead.`) } + Bip44Wallet.#isInternal = false super(seed, mnemonic, id) this.#pool = new Pool(Bip44Ckd) - Bip44Wallet.#isInternal = false } /** @@ -296,7 +296,6 @@ export class Bip44Wallet extends Wallet { */ static async create (key: CryptoKey, salt?: string): Promise static async create (passkey: string | CryptoKey, salt: string = ''): Promise { - Bip44Wallet.#isInternal = true try { const e = new Entropy() return await Bip44Wallet.fromEntropy(passkey as string, e.hex, salt) @@ -326,11 +325,11 @@ export class Bip44Wallet extends Wallet { */ static async fromEntropy (key: CryptoKey, entropy: string, salt?: string): Promise static async fromEntropy (passkey: string | CryptoKey, entropy: string, salt: string = ''): Promise { - Bip44Wallet.#isInternal = true try { const e = new Entropy(entropy) const m = await Bip39Mnemonic.fromEntropy(e.hex) const s = await m.toBip39Seed(salt) + Bip44Wallet.#isInternal = true const wallet = new this(s, m) await wallet.lock(passkey as string) return wallet @@ -358,10 +357,10 @@ export class Bip44Wallet extends Wallet { */ static async fromMnemonic (key: CryptoKey, mnemonic: string, salt?: string): Promise static async fromMnemonic (passkey: string | CryptoKey, mnemonic: string, salt: string = ''): Promise { - Bip44Wallet.#isInternal = true try { const m = await Bip39Mnemonic.fromPhrase(mnemonic) const s = await m.toBip39Seed(salt) + Bip44Wallet.#isInternal = true const wallet = new this(s, m) await wallet.lock(passkey as string) return wallet @@ -391,13 +390,13 @@ export class Bip44Wallet extends Wallet { */ static async fromSeed (key: CryptoKey, seed: string): Promise static async fromSeed (passkey: string | CryptoKey, seed: string): Promise { - Bip44Wallet.#isInternal = true if (seed.length !== SEED_LENGTH_BIP44) { throw new Error(`Expected a ${SEED_LENGTH_BIP44}-character seed, but received ${seed.length}-character string.`) } if (!/^[0-9a-fA-F]+$/i.test(seed)) { throw new Error('Seed contains invalid hexadecimal characters.') } + Bip44Wallet.#isInternal = true const wallet = new this(seed) await wallet.lock(passkey as string) return wallet @@ -410,12 +409,11 @@ export class Bip44Wallet extends Wallet { * @returns {Bip44Wallet} Restored locked Bip44Wallet */ static async restore (id: string): Promise { - Bip44Wallet.#isInternal = true if (typeof id !== 'string' || id === '') { throw new TypeError('Wallet ID is required to restore') } - const wallet = new this('', undefined, id) - return wallet + Bip44Wallet.#isInternal = true + return new this('', undefined, id) } /** @@ -455,8 +453,8 @@ export class Blake2bWallet extends Wallet { if (!Blake2bWallet.#isInternal) { throw new Error(`Blake2bWallet cannot be instantiated directly. Use 'await Blake2bWallet.create()' instead.`) } - super(seed, mnemonic, id) Blake2bWallet.#isInternal = false + super(seed, mnemonic, id) } /** @@ -476,7 +474,6 @@ export class Blake2bWallet extends Wallet { */ static async create (key: CryptoKey): Promise static async create (passkey: string | CryptoKey): Promise { - Blake2bWallet.#isInternal = true try { const seed = new Entropy() return await Blake2bWallet.fromSeed(passkey as string, seed.hex) @@ -504,7 +501,6 @@ export class Blake2bWallet extends Wallet { */ static async fromSeed (key: CryptoKey, seed: string): Promise static async fromSeed (passkey: string | CryptoKey, seed: string): Promise { - Blake2bWallet.#isInternal = true if (seed.length !== SEED_LENGTH_BLAKE2B) { throw new Error(`Expected a ${SEED_LENGTH_BLAKE2B}-character seed, but received ${seed.length}-character string.`) } @@ -513,6 +509,7 @@ export class Blake2bWallet extends Wallet { } const s = seed const m = await Bip39Mnemonic.fromEntropy(seed) + Blake2bWallet.#isInternal = true const wallet = new this(s, m) await wallet.lock(passkey as string) return wallet @@ -535,10 +532,10 @@ export class Blake2bWallet extends Wallet { */ static async fromMnemonic (key: CryptoKey, mnemonic: string): Promise static async fromMnemonic (passkey: string | CryptoKey, mnemonic: string): Promise { - Blake2bWallet.#isInternal = true try { const m = await Bip39Mnemonic.fromPhrase(mnemonic) const s = await m.toBlake2bSeed() + Blake2bWallet.#isInternal = true const wallet = new this(s, m) await wallet.lock(passkey as string) return wallet @@ -554,12 +551,11 @@ export class Blake2bWallet extends Wallet { * @returns {Blake2bWallet} Restored locked Blake2bWallet */ static async restore (id: string): Promise { - Blake2bWallet.#isInternal = true if (typeof id !== 'string' || id === '') { throw new TypeError('Wallet ID is required to restore') } - const wallet = new this('', undefined, id) - return wallet + Blake2bWallet.#isInternal = true + return new this('', undefined, id) } /** @@ -602,9 +598,9 @@ export class LedgerWallet extends Wallet { if (!LedgerWallet.#isInternal) { throw new Error(`LedgerWallet cannot be instantiated directly. Use 'await LedgerWallet.create()' instead.`) } + LedgerWallet.#isInternal = false super(undefined, undefined, id) this.#ledger = ledger - LedgerWallet.#isInternal = false } /** @@ -614,9 +610,9 @@ export class LedgerWallet extends Wallet { * @returns {LedgerWallet} A wallet containing accounts and a Ledger device communication object */ static async create (): Promise { - LedgerWallet.#isInternal = true const { Ledger } = await import('./ledger.js') const l = await Ledger.init() + LedgerWallet.#isInternal = true return new this(l) } @@ -627,12 +623,12 @@ export class LedgerWallet extends Wallet { * @returns {LedgerWallet} Restored LedgerWallet */ static async restore (id: string): Promise { - LedgerWallet.#isInternal = true if (typeof id !== 'string' || id === '') { throw new TypeError('Wallet ID is required to restore') } const { Ledger } = await import('./ledger.js') const l = await Ledger.init() + LedgerWallet.#isInternal = true return new this(l, id) } diff --git a/test.html b/test.html index 92bc8b0..f3aa38b 100644 --- a/test.html +++ b/test.html @@ -1,8 +1,10 @@ + - + diff --git a/test/GLOBALS.mjs b/test/GLOBALS.mjs index 1c780fd..410d2ae 100644 --- a/test/GLOBALS.mjs +++ b/test/GLOBALS.mjs @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: 2024 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later -import { EventEmitter } from 'node:events' -EventEmitter.defaultMaxListeners = navigator.hardwareConcurrency * 2 - if (globalThis.sessionStorage == null) { let _sessionStorage = {} Object.defineProperty(globalThis, 'sessionStorage', { @@ -18,3 +15,74 @@ if (globalThis.sessionStorage == null) { enumerable: true }) } + +export function skip (name, ...args) { + console.log(`SKIP: ${name}`) +} + +export function test (name, fn) { + if (fn instanceof Promise) { + try { + fn.then(() => console.log(`PASS: ${name}`)) + .catch((err) => console.error(`FAIL: ${name}`)) + } catch (err) { + console.error(`FAIL: ${name}`) + console.error(err) + } + } else { + try { + fn() + console.log(`PASS: ${name}`) + } catch (err) { + console.error(`FAIL: ${name}`) + console.error(err) + } + } +} + +export const assert = { + ok: (bool) => { + if (typeof bool !== 'boolean') throw new Error('Invalid assertion') + if (!bool) throw new Error(`test result falsy`) + return true + }, + exists: (a) => { + let b = a || null + if (b == null) throw new Error(`argument is ${typeof a}`) + return b != null + }, + equals: (a, b) => { + return a === b + }, + notEqual: (a, b) => { + return a !== b + }, + rejects: (fn, msg) => { + try { + if (!(fn instanceof Promise)) throw new Error(msg ?? 'expected async function') + fn.then(() => { throw new Error(msg ?? 'expected async function to reject') }) + .catch((err) => { return true }) + } catch (err) { + return true + } + }, + resolves: (fn, msg) => { + try { + if (!(fn instanceof Promise)) throw new Error('expected async function') + fn.then(() => { return true }) + .catch((err) => { throw new Error(msg ?? 'expected async function to resolve') }) + return true + } catch (err) { + throw new Error(msg ?? 'expected async function to resolve') + } + }, + throws: (fn, msg) => { + try { + const r = fn() + if (r instanceof Promise) throw new Error('expected synchronous function') + throw new Error(msg ?? `expected function to throw an exception`) + } catch (err) { + return true + } + } +} diff --git a/test/create-wallet.test.mjs b/test/create-wallet.test.mjs index 80ff375..ec7c448 100644 --- a/test/create-wallet.test.mjs +++ b/test/create-wallet.test.mjs @@ -3,62 +3,59 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, skip, test } from '#test/GLOBALS.mjs' import { NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Bip44Wallet, Blake2bWallet, LedgerWallet } from '#dist/main.js' -describe('creating a new wallet', async () => { - test('BIP-44 wallet with random entropy', async () => { - const wallet = await Bip44Wallet.create(NANO_TEST_VECTORS.PASSWORD) - await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) +test('BIP-44 wallet with random entropy', async () => { + const wallet = await Bip44Wallet.create(NANO_TEST_VECTORS.PASSWORD) + await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - expect('id' in wallet).toBeTruthy() - expect(/[A-Fa-f0-9]{32,64}/.test(wallet.id)).toBeTruthy() - expect('mnemonic' in wallet).toBeTruthy() - expect(/^(?:[a-z]{3,} ){11,23}[a-z]{3,}$/.test(wallet.mnemonic)).toBeTruthy() - expect('seed' in wallet).toBeTruthy() - expect(/[A-Fa-f0-9]{32,64}/.test(wallet.seed)).toBeTruthy() - }) - - test('BLAKE2b wallet with random entropy', async () => { - const wallet = await Blake2bWallet.create(NANO_TEST_VECTORS.PASSWORD) - await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) + assert.ok('id' in wallet) + assert.ok(/[A-Fa-f0-9]{32,64}/.test(wallet.id)) + assert.ok('mnemonic' in wallet) + assert.ok(/^(?:[a-z]{3,} ){11,23}[a-z]{3,}$/.test(wallet.mnemonic)) + assert.ok('seed' in wallet) + assert.ok(/[A-Fa-f0-9]{32,64}/.test(wallet.seed)) +}) - expect('id' in wallet).toBeTruthy() - expect(/[A-Fa-f0-9]{32,64}/.test(wallet.id)).toBeTruthy() - expect('mnemonic' in wallet).toBeTruthy() - expect(/^(?:[a-z]{3,} ){11,23}[a-z]{3,}$/.test(wallet.mnemonic)).toBeTruthy() - expect('seed' in wallet).toBeTruthy() - expect(/[A-Fa-f0-9]{32,64}/.test(wallet.seed)).toBeTruthy() - }) +test('BLAKE2b wallet with random entropy', async () => { + const wallet = await Blake2bWallet.create(NANO_TEST_VECTORS.PASSWORD) + await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - test('BIP-44 replace invalid salt with empty string', async () => { - const invalidArgs = [null, true, false, 0, 1, 2, { "foo": "bar" }] - for (const arg of invalidArgs) { - //@ts-expect-error - await assert.doesNotReject(Bip44Wallet.create(NANO_TEST_VECTORS.PASSWORD, arg), `Rejected ${arg}`) - } - }) + assert.ok('id' in wallet) + assert.ok(/[A-Fa-f0-9]{32,64}/.test(wallet.id)) + assert.ok('mnemonic' in wallet) + assert.ok(/^(?:[a-z]{3,} ){11,23}[a-z]{3,}$/.test(wallet.mnemonic)) + assert.ok('seed' in wallet) + assert.ok(/[A-Fa-f0-9]{32,64}/.test(wallet.seed)) +}) - test('fail when using new', async () => { +test('BIP-44 replace invalid salt with empty string', async () => { + const invalidArgs = [null, true, false, 0, 1, 2, { "foo": "bar" }] + for (const arg of invalidArgs) { //@ts-expect-error - expect(() => new Bip44Wallet()).toThrow() - //@ts-expect-error - expect(() => new Blake2bWallet()).toThrow() - //@ts-expect-error - expect(() => new LedgerWallet()).toThrow() - }) + assert.resolves(Bip44Wallet.create(NANO_TEST_VECTORS.PASSWORD, arg)) + } +}) - test('fail without a password', async () => { - //@ts-expect-error - expect(async () => await Bip44Wallet.create()).rejects() - //@ts-expect-error - expect(async () => await Blake2bWallet.create()).rejects() - }) +test('fail when using new', () => { + //@ts-expect-error + assert.throws(() => new Bip44Wallet()) + //@ts-expect-error + assert.throws(() => new Blake2bWallet()) + //@ts-expect-error + assert.throws(() => new LedgerWallet()) +}) + +test('fail without a password', async () => { + //@ts-expect-error + assert.rejects(Bip44Wallet.create()) + //@ts-expect-error + assert.rejects(Blake2bWallet.create()) +}) - test.skip('connect to ledger', async () => { - const wallet = await LedgerWallet.create() - expect(wallet).toBeDefined() - }) +skip('connect to ledger', async () => { + const wallet = await LedgerWallet.create() + assert.ok(wallet) }) diff --git a/test/derive-accounts.test.mjs b/test/derive-accounts.test.mjs index 9dc9513..44ecd26 100644 --- a/test/derive-accounts.test.mjs +++ b/test/derive-accounts.test.mjs @@ -3,46 +3,45 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, skip, test } from '#test/GLOBALS.mjs' import { NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Bip44Wallet, Blake2bWallet, LedgerWallet } from '#dist/main.js' -describe('derive child accounts from the same seed', async () => { +test('derive child accounts from the same seed', async () => { const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) test('should derive the first account from the given BIP-44 seed', async () => { const accounts = await wallet.accounts() - assert.equal(accounts.length, 1) - assert.equal(accounts[0].privateKey, NANO_TEST_VECTORS.PRIVATE_0) - assert.equal(accounts[0].publicKey, NANO_TEST_VECTORS.PUBLIC_0) - assert.equal(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_0) + assert.equals(accounts.length, 1) + assert.equals(accounts[0].privateKey, NANO_TEST_VECTORS.PRIVATE_0) + assert.equals(accounts[0].publicKey, NANO_TEST_VECTORS.PUBLIC_0) + assert.equals(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_0) }) test('should derive low indexed accounts from the given BIP-44 seed', async () => { const accounts = await wallet.accounts(1, 2) - assert.equal(accounts.length, 2) - assert.equal(accounts[0].privateKey, NANO_TEST_VECTORS.PRIVATE_1) - assert.equal(accounts[0].publicKey, NANO_TEST_VECTORS.PUBLIC_1) - assert.equal(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(accounts[1].privateKey, NANO_TEST_VECTORS.PRIVATE_2) - assert.equal(accounts[1].publicKey, NANO_TEST_VECTORS.PUBLIC_2) - assert.equal(accounts[1].address, NANO_TEST_VECTORS.ADDRESS_2) + assert.equals(accounts.length, 2) + assert.equals(accounts[0].privateKey, NANO_TEST_VECTORS.PRIVATE_1) + assert.equals(accounts[0].publicKey, NANO_TEST_VECTORS.PUBLIC_1) + assert.equals(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_1) + assert.equals(accounts[1].privateKey, NANO_TEST_VECTORS.PRIVATE_2) + assert.equals(accounts[1].publicKey, NANO_TEST_VECTORS.PUBLIC_2) + assert.equals(accounts[1].address, NANO_TEST_VECTORS.ADDRESS_2) }) test('should derive high indexed accounts from the given seed', async () => { const accounts = await wallet.accounts(0x70000000, 0x700000ff) - assert.equal(accounts.length, 0x100) + assert.equals(accounts.length, 0x100) for (const a of accounts) { - assert.ok(a) - assert.ok(a.address) - assert.ok(a.publicKey) - assert.ok(a.privateKey) - assert.ok(a.index != null) + assert.exists(a) + assert.exists(a.address) + assert.exists(a.publicKey) + assert.exists(a.privateKey) + assert.exists(a.index) } }) @@ -51,36 +50,36 @@ describe('derive child accounts from the same seed', async () => { await bwallet.unlock(NANO_TEST_VECTORS.PASSWORD) const lowAccounts = await bwallet.accounts(0, 2) - assert.equal(lowAccounts.length, 3) + assert.equals(lowAccounts.length, 3) for (const a of lowAccounts) { - assert.ok(a) - assert.ok(a.address) - assert.ok(a.publicKey) - assert.ok(a.privateKey) - assert.ok(a.index != null) + assert.exists(a) + assert.exists(a.address) + assert.exists(a.publicKey) + assert.exists(a.privateKey) + assert.exists(a.index) } const highAccounts = await bwallet.accounts(0x70000000, 0x700000ff) - assert.equal(highAccounts.length, 0x100) + assert.equals(highAccounts.length, 0x100) for (const a of highAccounts) { - assert.ok(a) - assert.ok(a.address) - assert.ok(a.publicKey) - assert.ok(a.privateKey) - assert.ok(a.index != null) + assert.exists(a) + assert.exists(a.address) + assert.exists(a.publicKey) + assert.exists(a.privateKey) + assert.exists(a.index) } }) }) -describe.skip('Ledger device accounts', async () => { +skip('Ledger device accounts', async () => { const wallet = await LedgerWallet.create() test('should fetch the first account from a Ledger device', async () => { const accounts = await wallet.accounts() - assert.equal(accounts.length, 1) - assert.ok(accounts[0].publicKey) - assert.ok(accounts[0].address) + assert.equals(accounts.length, 1) + assert.exists(accounts[0].publicKey) + assert.exists(accounts[0].address) }) }) diff --git a/test/import-wallet.test.mjs b/test/import-wallet.test.mjs index 56d913a..1585620 100644 --- a/test/import-wallet.test.mjs +++ b/test/import-wallet.test.mjs @@ -3,12 +3,11 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, test } from '#test/GLOBALS.mjs' import { BIP32_TEST_VECTORS, CUSTOM_TEST_VECTORS, NANO_TEST_VECTORS, TREZOR_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Account, Bip44Wallet, Blake2bWallet } from '#dist/main.js' -describe('import wallet with test vectors test', () => { +test('import wallet with test vectors test', () => { test('should successfully import a wallet with the official Nano test vectors mnemonic', async () => { const wallet = await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) @@ -17,17 +16,17 @@ describe('import wallet with test vectors test', () => { assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) assert.ok(accounts[0] instanceof Account) - assert.equal(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) - assert.equal(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) - assert.equal(accounts[0].privateKey, NANO_TEST_VECTORS.PRIVATE_0) - assert.equal(accounts[0].publicKey, NANO_TEST_VECTORS.PUBLIC_0) - assert.equal(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_0) + assert.equals(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) + assert.equals(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) + assert.equals(accounts[0].privateKey, NANO_TEST_VECTORS.PRIVATE_0) + assert.equals(accounts[0].publicKey, NANO_TEST_VECTORS.PUBLIC_0) + assert.equals(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_0) }) test('should successfully import a wallet with the checksum starting with a zero', async () => { const wallet = await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, 'food define cancel major spoon trash cigar basic aim bless wolf win ability seek paddle bench seed century group they mercy address monkey cake') await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - assert.equal(wallet.seed, 'F665F804E5907985455D1E5A7AD344843A2ED4179A7E06EEF263DE925FF6F4C0991B0A9344FCEE939FE0F1B1841B8C9B20FEACF6B954B74B2D26A01906B758E2') + assert.equals(wallet.seed, 'F665F804E5907985455D1E5A7AD344843A2ED4179A7E06EEF263DE925FF6F4C0991B0A9344FCEE939FE0F1B1841B8C9B20FEACF6B954B74B2D26A01906B758E2') }) test('should successfully import a wallet with a 12-word phrase', async () => { @@ -36,11 +35,11 @@ describe('import wallet with test vectors test', () => { const accounts = await wallet.accounts() const account = accounts[0] - assert.equal(wallet.mnemonic, CUSTOM_TEST_VECTORS.MNEMONIC_0) - assert.equal(wallet.seed, CUSTOM_TEST_VECTORS.SEED_0) - assert.equal(account.privateKey, CUSTOM_TEST_VECTORS.PRIVATE_0) - assert.equal(account.publicKey, CUSTOM_TEST_VECTORS.PUBLIC_0) - assert.equal(account.address, CUSTOM_TEST_VECTORS.ADDRESS_0) + assert.equals(wallet.mnemonic, CUSTOM_TEST_VECTORS.MNEMONIC_0) + assert.equals(wallet.seed, CUSTOM_TEST_VECTORS.SEED_0) + assert.equals(account.privateKey, CUSTOM_TEST_VECTORS.PRIVATE_0) + assert.equals(account.publicKey, CUSTOM_TEST_VECTORS.PUBLIC_0) + assert.equals(account.address, CUSTOM_TEST_VECTORS.ADDRESS_0) }) test('should successfully import a wallet with a 15-word phrase', async () => { @@ -49,11 +48,11 @@ describe('import wallet with test vectors test', () => { const accounts = await wallet.accounts() const account = accounts[0] - assert.equal(wallet.mnemonic, CUSTOM_TEST_VECTORS.MNEMONIC_1) - assert.equal(wallet.seed, CUSTOM_TEST_VECTORS.SEED_1) - assert.equal(account.privateKey, CUSTOM_TEST_VECTORS.PRIVATE_1) - assert.equal(account.publicKey, CUSTOM_TEST_VECTORS.PUBLIC_1) - assert.equal(account.address, CUSTOM_TEST_VECTORS.ADDRESS_1) + assert.equals(wallet.mnemonic, CUSTOM_TEST_VECTORS.MNEMONIC_1) + assert.equals(wallet.seed, CUSTOM_TEST_VECTORS.SEED_1) + assert.equals(account.privateKey, CUSTOM_TEST_VECTORS.PRIVATE_1) + assert.equals(account.publicKey, CUSTOM_TEST_VECTORS.PUBLIC_1) + assert.equals(account.address, CUSTOM_TEST_VECTORS.ADDRESS_1) }) test('should successfully import a wallet with a 18-word phrase', async () => { @@ -62,11 +61,11 @@ describe('import wallet with test vectors test', () => { const accounts = await wallet.accounts() const account = accounts[0] - assert.equal(wallet.mnemonic, CUSTOM_TEST_VECTORS.MNEMONIC_2) - assert.equal(wallet.seed, CUSTOM_TEST_VECTORS.SEED_2) - assert.equal(account.privateKey, CUSTOM_TEST_VECTORS.PRIVATE_2) - assert.equal(account.publicKey, CUSTOM_TEST_VECTORS.PUBLIC_2) - assert.equal(account.address, CUSTOM_TEST_VECTORS.ADDRESS_2) + assert.equals(wallet.mnemonic, CUSTOM_TEST_VECTORS.MNEMONIC_2) + assert.equals(wallet.seed, CUSTOM_TEST_VECTORS.SEED_2) + assert.equals(account.privateKey, CUSTOM_TEST_VECTORS.PRIVATE_2) + assert.equals(account.publicKey, CUSTOM_TEST_VECTORS.PUBLIC_2) + assert.equals(account.address, CUSTOM_TEST_VECTORS.ADDRESS_2) }) test('should successfully import a wallet with a 21-word phrase', async () => { @@ -75,11 +74,11 @@ describe('import wallet with test vectors test', () => { const accounts = await wallet.accounts() const account = accounts[0] - assert.equal(wallet.mnemonic, CUSTOM_TEST_VECTORS.MNEMONIC_3) - assert.equal(wallet.seed, CUSTOM_TEST_VECTORS.SEED_3) - assert.equal(account.privateKey, CUSTOM_TEST_VECTORS.PRIVATE_3) - assert.equal(account.publicKey, CUSTOM_TEST_VECTORS.PUBLIC_3) - assert.equal(account.address, CUSTOM_TEST_VECTORS.ADDRESS_3) + assert.equals(wallet.mnemonic, CUSTOM_TEST_VECTORS.MNEMONIC_3) + assert.equals(wallet.seed, CUSTOM_TEST_VECTORS.SEED_3) + assert.equals(account.privateKey, CUSTOM_TEST_VECTORS.PRIVATE_3) + assert.equals(account.publicKey, CUSTOM_TEST_VECTORS.PUBLIC_3) + assert.equals(account.address, CUSTOM_TEST_VECTORS.ADDRESS_3) }) test('should successfully import a wallet with the official Nano test vectors seed', async () => { @@ -90,11 +89,11 @@ describe('import wallet with test vectors test', () => { assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) assert.ok(accounts[0] instanceof Account) - assert.equal(wallet.mnemonic, '') - assert.equal(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) - assert.equal(accounts[0].privateKey, NANO_TEST_VECTORS.PRIVATE_0) - assert.equal(accounts[0].publicKey, NANO_TEST_VECTORS.PUBLIC_0) - assert.equal(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_0) + assert.equals(wallet.mnemonic, '') + assert.equals(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) + assert.equals(accounts[0].privateKey, NANO_TEST_VECTORS.PRIVATE_0) + assert.equals(accounts[0].publicKey, NANO_TEST_VECTORS.PUBLIC_0) + assert.equals(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_0) }) test('should successfully import a BIP-44 wallet with the zero seed', async () => { @@ -104,15 +103,15 @@ describe('import wallet with test vectors test', () => { assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_0) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.SEED_0.toUpperCase()) - assert.equal(accounts.length, 4) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_0) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.SEED_0.toUpperCase()) + assert.equals(accounts.length, 4) for (let i = 0; i < accounts.length; i++) { assert.ok(accounts[i]) assert.ok(accounts[i].address) assert.ok(accounts[i].publicKey) assert.ok(accounts[i].privateKey) - assert.equal(accounts[i].index, i) + assert.equals(accounts[i].index, i) } }) @@ -123,15 +122,15 @@ describe('import wallet with test vectors test', () => { assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_0) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_0) - assert.equal(accounts.length, 4) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_0) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_0) + assert.equals(accounts.length, 4) for (let i = 0; i < accounts.length; i++) { assert.ok(accounts[i]) assert.ok(accounts[i].address) assert.ok(accounts[i].publicKey) assert.ok(accounts[i].privateKey) - assert.equal(accounts[i].index, i) + assert.equals(accounts[i].index, i) } }) @@ -142,18 +141,18 @@ describe('import wallet with test vectors test', () => { assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1) assert.ok(accounts[0] instanceof Account) - assert.equal(accounts[0].index, 0) - assert.equal(accounts[0].privateKey, TREZOR_TEST_VECTORS.BLAKE2B_1_PRIVATE_0) - assert.equal(accounts[0].publicKey, TREZOR_TEST_VECTORS.BLAKE2B_1_PUBLIC_0) - assert.equal(accounts[0].address, TREZOR_TEST_VECTORS.BLAKE2B_1_ADDRESS_0) + assert.equals(accounts[0].index, 0) + assert.equals(accounts[0].privateKey, TREZOR_TEST_VECTORS.BLAKE2B_1_PRIVATE_0) + assert.equals(accounts[0].publicKey, TREZOR_TEST_VECTORS.BLAKE2B_1_PUBLIC_0) + assert.equals(accounts[0].address, TREZOR_TEST_VECTORS.BLAKE2B_1_ADDRESS_0) assert.ok(accounts[1] instanceof Account) - assert.equal(accounts[1].index, 1) - assert.equal(accounts[1].privateKey, TREZOR_TEST_VECTORS.BLAKE2B_1_PRIVATE_1) - assert.equal(accounts[1].publicKey, TREZOR_TEST_VECTORS.BLAKE2B_1_PUBLIC_1) - assert.equal(accounts[1].address, TREZOR_TEST_VECTORS.BLAKE2B_1_ADDRESS_1) + assert.equals(accounts[1].index, 1) + assert.equals(accounts[1].privateKey, TREZOR_TEST_VECTORS.BLAKE2B_1_PRIVATE_1) + assert.equals(accounts[1].publicKey, TREZOR_TEST_VECTORS.BLAKE2B_1_PUBLIC_1) + assert.equals(accounts[1].address, TREZOR_TEST_VECTORS.BLAKE2B_1_ADDRESS_1) }) test('should get identical BLAKE2b wallets when created with a seed versus with its derived mnemonic', async () => { @@ -165,17 +164,17 @@ describe('import wallet with test vectors test', () => { assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) assert.ok(walletAccount) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_2) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_2) const imported = await Blake2bWallet.fromMnemonic(TREZOR_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.MNEMONIC_2) await imported.unlock(TREZOR_TEST_VECTORS.PASSWORD) const importedAccounts = await imported.accounts() const importedAccount = importedAccounts[0] - assert.equal(imported.mnemonic, wallet.mnemonic) - assert.equal(imported.seed, wallet.seed) - assert.equal(importedAccount.privateKey, walletAccount.privateKey) - assert.equal(importedAccount.publicKey, walletAccount.publicKey) + assert.equals(imported.mnemonic, wallet.mnemonic) + assert.equals(imported.seed, wallet.seed) + assert.equals(importedAccount.privateKey, walletAccount.privateKey) + assert.equals(importedAccount.publicKey, walletAccount.publicKey) }) test('should get identical BLAKE2b wallets when created with max entropy value', async () => { @@ -186,16 +185,16 @@ describe('import wallet with test vectors test', () => { assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) assert.ok(accounts[0] instanceof Account) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_3) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_3) - assert.equal(accounts[0].index, 0) - assert.equal(accounts[0].privateKey, TREZOR_TEST_VECTORS.BLAKE2B_3_PRIVATE_0) - assert.equal(accounts[0].publicKey, TREZOR_TEST_VECTORS.BLAKE2B_3_PUBLIC_0) - assert.equal(accounts[0].address, TREZOR_TEST_VECTORS.BLAKE2B_3_ADDRESS_0) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_3) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_3) + assert.equals(accounts[0].index, 0) + assert.equals(accounts[0].privateKey, TREZOR_TEST_VECTORS.BLAKE2B_3_PRIVATE_0) + assert.equals(accounts[0].publicKey, TREZOR_TEST_VECTORS.BLAKE2B_3_PUBLIC_0) + assert.equals(accounts[0].address, TREZOR_TEST_VECTORS.BLAKE2B_3_ADDRESS_0) }) }) -describe('invalid wallet', async () => { +test('invalid wallet', async () => { test('throw when given invalid entropy', async () => { assert.rejects(async () => await Bip44Wallet.fromEntropy(NANO_TEST_VECTORS.PASSWORD, '6CAF5A42BB8074314AAE20295975ECE663BE7AAD945A73613D193B0CC41C797')) assert.rejects(async () => await Bip44Wallet.fromEntropy(NANO_TEST_VECTORS.PASSWORD, '6CAF5A42BB8074314AAE20295975ECE663BE7AAD945A73613D193B0CC41C79701')) @@ -204,36 +203,36 @@ describe('invalid wallet', async () => { test('should throw when given a seed with an invalid length', async () => { await assert.rejects(Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED + 'f'), - { message: `Expected a ${NANO_TEST_VECTORS.BIP39_SEED.length}-character seed, but received ${NANO_TEST_VECTORS.BIP39_SEED.length + 1}-character string.` }) + `Expected a ${NANO_TEST_VECTORS.BIP39_SEED.length}-character seed, but received ${NANO_TEST_VECTORS.BIP39_SEED.length + 1}-character string.`) await assert.rejects(Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED.slice(0, -1)), - { message: `Expected a ${NANO_TEST_VECTORS.BIP39_SEED.length}-character seed, but received ${NANO_TEST_VECTORS.BIP39_SEED.length - 1}-character string.` }) + `Expected a ${NANO_TEST_VECTORS.BIP39_SEED.length}-character seed, but received ${NANO_TEST_VECTORS.BIP39_SEED.length - 1}-character string.`) }) test('should throw when given a seed containing non-hex characters', async () => { await assert.rejects(Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.SEED_0.replace(/./, 'g')), - { message: 'Seed contains invalid hexadecimal characters.' }) + 'Seed contains invalid hexadecimal characters.') await assert.rejects(Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_1.replace(/./, 'g')), - { message: 'Seed contains invalid hexadecimal characters.' }) + 'Seed contains invalid hexadecimal characters.') }) }) -describe('import from storage', async () => { +test('import from storage', async () => { test('should retrieve a Bip44Wallet from storage using an ID', async () => { const id = (await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD)).id const wallet = await Bip44Wallet.restore(id) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, '') - assert.equal(wallet.seed, '') + assert.equals(wallet.mnemonic, '') + assert.equals(wallet.seed, '') const unlockResult = await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - assert.equal(unlockResult, true) + assert.equals(unlockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) - assert.equal(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) + assert.equals(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) + assert.equals(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) }) test('should retrieve a Blake2bWallet from storage using an ID', async () => { @@ -242,15 +241,15 @@ describe('import from storage', async () => { assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, '') - assert.equal(wallet.seed, '') + assert.equals(wallet.mnemonic, '') + assert.equals(wallet.seed, '') const unlockResult = await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - assert.equal(unlockResult, true) + assert.equals(unlockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_0) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_0) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_0) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_0) }) }) diff --git a/test/lock-unlock-wallet.mjs b/test/lock-unlock-wallet.mjs index 3c271e5..b583555 100644 --- a/test/lock-unlock-wallet.mjs +++ b/test/lock-unlock-wallet.mjs @@ -3,27 +3,26 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, test } from '#test/GLOBALS.mjs' import { NANO_TEST_VECTORS, TREZOR_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Bip44Wallet, Blake2bWallet } from '#dist/main.js' -describe('locking and unlocking a Bip44Wallet', async () => { +test('locking and unlocking a Bip44Wallet', async () => { test('should succeed with a password', async () => { const wallet = await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, '') - assert.equal(wallet.seed, '') + assert.equals(wallet.mnemonic, '') + assert.equals(wallet.seed, '') const unlockResult = await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - assert.equal(unlockResult, true) + assert.equals(unlockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) - assert.equal(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) + assert.equals(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) + assert.equals(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) }) test('should succeed with a random CryptoKey', async () => { @@ -35,16 +34,16 @@ describe('locking and unlocking a Bip44Wallet', async () => { assert.ok(lockResult) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, '') - assert.equal(wallet.seed, '') + assert.equals(wallet.mnemonic, '') + assert.equals(wallet.seed, '') const unlockResult = await wallet.unlock(key) - assert.equal(unlockResult, true) + assert.equals(unlockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) - assert.equal(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) + assert.equals(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) + assert.equals(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) }) test('should fail to unlock with different passwords', async () => { @@ -53,7 +52,7 @@ describe('locking and unlocking a Bip44Wallet', async () => { const lockResult = await wallet.lock(TREZOR_TEST_VECTORS.PASSWORD) await assert.rejects(wallet.unlock(NANO_TEST_VECTORS.PASSWORD), { message: 'Failed to unlock wallet' }) - assert.equal(lockResult, true) + assert.equals(lockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) assert.notEqual(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) @@ -68,7 +67,7 @@ describe('locking and unlocking a Bip44Wallet', async () => { const lockResult = await wallet.lock(rightKey) await assert.rejects(wallet.unlock(wrongKey), { message: 'Failed to unlock wallet' }) - assert.equal(lockResult, true) + assert.equals(lockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) assert.notEqual(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) @@ -94,8 +93,8 @@ describe('locking and unlocking a Bip44Wallet', async () => { await assert.rejects(wallet.lock(), { message: 'Failed to lock wallet' }) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) - assert.equal(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) + assert.equals(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) + assert.equals(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) await wallet.lock('password') @@ -115,8 +114,8 @@ describe('locking and unlocking a Bip44Wallet', async () => { await assert.rejects(wallet.lock(1), { message: 'Failed to lock wallet' }) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) - assert.equal(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) + assert.equals(wallet.mnemonic, NANO_TEST_VECTORS.MNEMONIC) + assert.equals(wallet.seed, NANO_TEST_VECTORS.BIP39_SEED) await wallet.lock(NANO_TEST_VECTORS.PASSWORD) @@ -129,22 +128,22 @@ describe('locking and unlocking a Bip44Wallet', async () => { }) }) -describe('locking and unlocking a Blake2bWallet', async () => { +test('locking and unlocking a Blake2bWallet', async () => { test('should succeed with a password', async () => { const wallet = await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_0) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, '') - assert.equal(wallet.seed, '') + assert.equals(wallet.mnemonic, '') + assert.equals(wallet.seed, '') const unlockResult = await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) - assert.equal(unlockResult, true) + assert.equals(unlockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_0) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_0) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_0) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_0) }) test('should succeed with a random CryptoKey', async () => { @@ -153,20 +152,20 @@ describe('locking and unlocking a Blake2bWallet', async () => { const key = await globalThis.crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']) const lockResult = await wallet.lock(key) - assert.equal(lockResult, true) + assert.equals(lockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, '') - assert.equal(wallet.seed, '') + assert.equals(wallet.mnemonic, '') + assert.equals(wallet.seed, '') const unlockResult = await wallet.unlock(key) - assert.equal(lockResult, true) - assert.equal(unlockResult, true) + assert.equals(lockResult, true) + assert.equals(unlockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1) }) test('should fail to unlock with different passwords', async () => { @@ -187,7 +186,7 @@ describe('locking and unlocking a Blake2bWallet', async () => { const lockResult = await wallet.lock(rightKey) await assert.rejects(wallet.unlock(wrongKey), { message: 'Failed to unlock wallet' }) - assert.equal(lockResult, true) + assert.equals(lockResult, true) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) assert.notEqual(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) @@ -213,8 +212,8 @@ describe('locking and unlocking a Blake2bWallet', async () => { await assert.rejects(wallet.lock(), { message: 'Failed to lock wallet' }) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1) await wallet.lock(NANO_TEST_VECTORS.PASSWORD) @@ -234,8 +233,8 @@ describe('locking and unlocking a Blake2bWallet', async () => { await assert.rejects(wallet.lock(1), { message: 'Failed to lock wallet' }) assert.ok('mnemonic' in wallet) assert.ok('seed' in wallet) - assert.equal(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) - assert.equal(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1) + assert.equals(wallet.mnemonic, TREZOR_TEST_VECTORS.MNEMONIC_1) + assert.equals(wallet.seed, TREZOR_TEST_VECTORS.ENTROPY_1) await wallet.lock(NANO_TEST_VECTORS.PASSWORD) diff --git a/test/main.mjs b/test/main.mjs new file mode 100644 index 0000000..f7a990f --- /dev/null +++ b/test/main.mjs @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2024 Chris Duncan +// SPDX-License-Identifier: GPL-3.0-or-later + +import './create-wallet.test.mjs' +import './derive-accounts.test.mjs' +import './import-wallet.test.mjs' +import './lock-unlock-wallet.mjs' +import './manage-rolodex.mjs' +import './refresh-accounts.test.mjs' +import './sign-blocks.test.mjs' +import './tools.test.mjs' + +import './perf/account.perf.js' +import './perf/wallet.perf.js' + +console.log('> TESTING COMPLETE <') diff --git a/test/manage-rolodex.mjs b/test/manage-rolodex.mjs index e15d22d..53c0d27 100644 --- a/test/manage-rolodex.mjs +++ b/test/manage-rolodex.mjs @@ -3,31 +3,30 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, test } from '#test/GLOBALS.mjs' import { NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Rolodex, Tools } from '#dist/main.js' -describe('rolodex valid contact management', async () => { +test('rolodex valid contact management', async () => { test('should create a rolodex and add two contacts', async () => { const rolodex = new Rolodex() - assert.equal(rolodex.constructor, Rolodex) + assert.equals(rolodex.constructor, Rolodex) await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0) await rolodex.add('JaneSmith', NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(rolodex.getAllNames().length, 2) - assert.equal(rolodex.getAllNames()[0], 'JohnDoe') - assert.equal(rolodex.getAllNames()[1], 'JaneSmith') - assert.equal(rolodex.getAddresses('JohnDoe').length, 1) - assert.equal(rolodex.getAddresses('JohnDoe')[0], NANO_TEST_VECTORS.ADDRESS_0) - assert.equal(rolodex.getAddresses('JaneSmith').length, 1) - assert.equal(rolodex.getAddresses('JaneSmith')[0], NANO_TEST_VECTORS.ADDRESS_1) + assert.equals(rolodex.getAllNames().length, 2) + assert.equals(rolodex.getAllNames()[0], 'JohnDoe') + assert.equals(rolodex.getAllNames()[1], 'JaneSmith') + assert.equals(rolodex.getAddresses('JohnDoe').length, 1) + assert.equals(rolodex.getAddresses('JohnDoe')[0], NANO_TEST_VECTORS.ADDRESS_0) + assert.equals(rolodex.getAddresses('JaneSmith').length, 1) + assert.equals(rolodex.getAddresses('JaneSmith')[0], NANO_TEST_VECTORS.ADDRESS_1) }) test('should get a name from an address', async () => { const rolodex = new Rolodex() await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0) - assert.equal(rolodex.getName(NANO_TEST_VECTORS.ADDRESS_0), 'JohnDoe') + assert.equals(rolodex.getName(NANO_TEST_VECTORS.ADDRESS_0), 'JohnDoe') }) test('should add three addresses to the same contact', async () => { @@ -35,46 +34,46 @@ describe('rolodex valid contact management', async () => { await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_1) await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_2) await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0) - assert.equal(rolodex.getAddresses('JohnDoe').length, 3) - assert.equal(rolodex.getAddresses('JohnDoe')[0], NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(rolodex.getAddresses('JohnDoe')[1], NANO_TEST_VECTORS.ADDRESS_2) - assert.equal(rolodex.getAddresses('JohnDoe')[2], NANO_TEST_VECTORS.ADDRESS_0) + assert.equals(rolodex.getAddresses('JohnDoe').length, 3) + assert.equals(rolodex.getAddresses('JohnDoe')[0], NANO_TEST_VECTORS.ADDRESS_1) + assert.equals(rolodex.getAddresses('JohnDoe')[1], NANO_TEST_VECTORS.ADDRESS_2) + assert.equals(rolodex.getAddresses('JohnDoe')[2], NANO_TEST_VECTORS.ADDRESS_0) }) test('should update the name on an existing entry', async () => { const rolodex = new Rolodex() await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0) await rolodex.add('JaneSmith', NANO_TEST_VECTORS.ADDRESS_0) - assert.equal(rolodex.getAddresses('JohnDoe').length, 0) - assert.equal(rolodex.getAddresses('JaneSmith').length, 1) - assert.equal(rolodex.getAddresses('JaneSmith')[0], NANO_TEST_VECTORS.ADDRESS_0) + assert.equals(rolodex.getAddresses('JohnDoe').length, 0) + assert.equals(rolodex.getAddresses('JaneSmith').length, 1) + assert.equals(rolodex.getAddresses('JaneSmith')[0], NANO_TEST_VECTORS.ADDRESS_0) }) test('should return empty address array for an unknown contact', async () => { const rolodex = new Rolodex() await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0) - assert.equal(Array.isArray(rolodex.getAddresses('JaneSmith')), true) - assert.equal(rolodex.getAddresses('JaneSmith').length, 0) + assert.equals(Array.isArray(rolodex.getAddresses('JaneSmith')), true) + assert.equals(rolodex.getAddresses('JaneSmith').length, 0) }) test('should return empty address array for blank contact names', () => { const rolodex = new Rolodex() //@ts-expect-error - assert.equal(Array.isArray(rolodex.getAddresses(undefined)), true) + assert.equals(Array.isArray(rolodex.getAddresses(undefined)), true) //@ts-expect-error - assert.equal(rolodex.getAddresses(undefined).length, 0) + assert.equals(rolodex.getAddresses(undefined).length, 0) //@ts-expect-error - assert.equal(Array.isArray(rolodex.getAddresses(null)), true) + assert.equals(Array.isArray(rolodex.getAddresses(null)), true) //@ts-expect-error - assert.equal(rolodex.getAddresses(null).length, 0) - assert.equal(Array.isArray(rolodex.getAddresses('')), true) - assert.equal(rolodex.getAddresses('').length, 0) + assert.equals(rolodex.getAddresses(null).length, 0) + assert.equals(Array.isArray(rolodex.getAddresses('')), true) + assert.equals(rolodex.getAddresses('').length, 0) }) test('should return null for an unknown address', async () => { const rolodex = new Rolodex() await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0) - assert.equal(rolodex.getName(NANO_TEST_VECTORS.ADDRESS_1), null) + assert.equals(rolodex.getName(NANO_TEST_VECTORS.ADDRESS_1), null) assert.notEqual(rolodex.getName(NANO_TEST_VECTORS.ADDRESS_1), undefined) }) @@ -82,19 +81,19 @@ describe('rolodex valid contact management', async () => { const rolodex = new Rolodex() await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0) //@ts-expect-error - assert.equal(rolodex.getName(undefined), null) + assert.equals(rolodex.getName(undefined), null) //@ts-expect-error assert.notEqual(rolodex.getName(undefined), undefined) //@ts-expect-error - assert.equal(rolodex.getName(null), null) + assert.equals(rolodex.getName(null), null) //@ts-expect-error assert.notEqual(rolodex.getName(null), undefined) - assert.equal(rolodex.getName(''), null) + assert.equals(rolodex.getName(''), null) assert.notEqual(rolodex.getName(''), undefined) }) }) -describe('rolodex exceptions', async () => { +test('rolodex exceptions', async () => { test('should throw if adding no data', async () => { const rolodex = new Rolodex() //@ts-expect-error @@ -122,7 +121,7 @@ describe('rolodex exceptions', async () => { }) }) -describe('rolodex data signature verification', async () => { +test('rolodex data signature verification', async () => { const data = 'Test data' const signature = await Tools.sign(NANO_TEST_VECTORS.PRIVATE_0, data) const rolodex = new Rolodex() @@ -130,12 +129,12 @@ describe('rolodex data signature verification', async () => { test('should verify valid data and signature', async () => { await rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0) const result = await rolodex.verify('JohnDoe', signature, data) - assert.equal(result, true) + assert.equals(result, true) }) test('should reject incorrect contact for signature', async () => { await rolodex.add('JaneSmith', NANO_TEST_VECTORS.ADDRESS_1) const result = await rolodex.verify('JaneSmith', signature, data) - assert.equal(result, false) + assert.equals(result, false) }) }) diff --git a/perf/account.perf.js b/test/perf/account.perf.js similarity index 67% rename from perf/account.perf.js rename to test/perf/account.perf.js index 831f275..e4d796a 100644 --- a/perf/account.perf.js +++ b/test/perf/account.perf.js @@ -3,27 +3,25 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, skip, test } from '#test/GLOBALS.mjs' import { NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Bip44Wallet, Blake2bWallet } from '#dist/main.js' -console.log('child key derivation performance test') - -test('BIP-44 ckd', async () => { +test('BIP-44 ckd performance test', async () => { const wallet = await Bip44Wallet.create(NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) + console.log(`HERE`) const accounts = await wallet.accounts(0, 0x7fff) - expect(accounts.length).toEqual(0x8000) + assert.equals(accounts.length, 0x8000) }) -test('BLAKE2b ckd', async () => { +test('BLAKE2b ckd performance test', async () => { const wallet = await Blake2bWallet.create(NANO_TEST_VECTORS.PASSWORD) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) const accounts = await wallet.accounts(0, 0x7fff) - expect(accounts.length).toEqual(0x8000) + assert.equals(accounts.length, 0x8000) }) diff --git a/perf/wallet.perf.js b/test/perf/wallet.perf.js similarity index 62% rename from perf/wallet.perf.js rename to test/perf/wallet.perf.js index 51cb4a8..7c65a68 100644 --- a/perf/wallet.perf.js +++ b/test/perf/wallet.perf.js @@ -3,25 +3,22 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, skip, test } from '#test/GLOBALS.mjs' import { NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Bip44Wallet, Blake2bWallet } from '#dist/main.js' -console.log('wallet performance test') - -test.skip('creating BIP-44 wallets', async () => { +test('creating BIP-44 wallets performance test', async () => { const wallets = [] for (let i = 0x80; i > 0; i--) { wallets.push(await Bip44Wallet.create(NANO_TEST_VECTORS.PASSWORD)) } - expect(wallets.length).toEqual(0x80) + assert.equals(wallets.length, 0x80) }) -test.skip('creating BLAKE2b wallets', async () => { +test('creating BLAKE2b wallets performance test', async () => { const wallets = [] for (let i = 0x80; i > 0; i--) { wallets.push(await Blake2bWallet.create(NANO_TEST_VECTORS.PASSWORD)) } - expect(wallets.length).toEqual(0x80) + assert.equals(wallets.length, 0x80) }) diff --git a/test/refresh-accounts.test.mjs b/test/refresh-accounts.test.mjs index 23010d9..00e76fa 100644 --- a/test/refresh-accounts.test.mjs +++ b/test/refresh-accounts.test.mjs @@ -3,34 +3,39 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, skip, test } from '#test/GLOBALS.mjs' import { NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Account, Bip44Wallet, Rpc } from '#dist/main.js' const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) -const node = new Rpc(process.env.NODE_URL ?? '', process.env.API_KEY_NAME) - -describe.skip('refreshing account info', async () => { +let rpc +//@ts-expect-error +var process = process || null +if (process) { + //@ts-expect-error + rpc = new Rpc(process?.env?.NODE_URL ?? '', process?.env?.API_KEY_NAME) +} + +skip('refreshing account info', async () => { test('should fetch balance, frontier, and representative', async () => { const accounts = await wallet.accounts() const account = accounts[0] - await account.refresh(node) + await account.refresh(rpc) - assert.equal(typeof account.balance, 'bigint') + assert.equals(typeof account.balance, 'bigint') assert.notEqual(account.balance, undefined) assert.notEqual(account.balance, null) assert.notEqual(account.balance, '') assert.notEqual(account.balance && account.balance < 0, true) - assert.equal(typeof account.frontier, 'string') + assert.equals(typeof account.frontier, 'string') assert.notEqual(account.frontier, undefined) assert.notEqual(account.frontier, null) assert.notEqual(account.frontier, '') assert.match(account.frontier ?? '', /^[0-9A-F]{64}$/i) - assert.equal(account.representative && account.representative.constructor, Account) + assert.equals(account.representative && account.representative.constructor, Account) assert.notEqual(account.representative, undefined) assert.notEqual(account.representative, null) assert.notEqual(account.representative, '') @@ -42,7 +47,7 @@ describe.skip('refreshing account info', async () => { test('should throw when refreshing unopened account', async () => { const accounts = await wallet.accounts(0x7fffffff) const account = accounts[0] - await assert.rejects(account.refresh(node), + await assert.rejects(account.refresh(rpc), { message: 'Account not found' }) }) @@ -60,26 +65,26 @@ describe.skip('refreshing account info', async () => { }) }) -describe.skip('finding next unopened account', async () => { +skip('finding next unopened account', async () => { test('should return correct account from test vector', async () => { - const account = await wallet.getNextNewAccount(node) + const account = await wallet.getNextNewAccount(rpc) assert.ok(account) - assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) + assert.equals(account.address, NANO_TEST_VECTORS.ADDRESS_1) + assert.equals(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) }) test('should return successfully for small batch size', async () => { - const account = await wallet.getNextNewAccount(node, 1) + const account = await wallet.getNextNewAccount(rpc, 1) assert.ok(account) - assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) + assert.equals(account.address, NANO_TEST_VECTORS.ADDRESS_1) + assert.equals(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) }) test('should return successfully for large batch size', async () => { - const account = await wallet.getNextNewAccount(node, 100) + const account = await wallet.getNextNewAccount(rpc, 100) assert.ok(account) - assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) + assert.equals(account.address, NANO_TEST_VECTORS.ADDRESS_1) + assert.equals(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) }) test('should throw on invalid node URL', async () => { @@ -97,35 +102,35 @@ describe.skip('finding next unopened account', async () => { test('should throw on invalid batch size', async () => { //@ts-expect-error - await assert.rejects(wallet.getNextNewAccount(node, null)) - await assert.rejects(wallet.getNextNewAccount(node, -1)) + await assert.rejects(wallet.getNextNewAccount(rpc, null)) + await assert.rejects(wallet.getNextNewAccount(rpc, -1)) //@ts-expect-error - await assert.rejects(wallet.getNextNewAccount(node, '')) + await assert.rejects(wallet.getNextNewAccount(rpc, '')) //@ts-expect-error - await assert.rejects(wallet.getNextNewAccount(node, 'foo')) + await assert.rejects(wallet.getNextNewAccount(rpc, 'foo')) //@ts-expect-error - await assert.rejects(wallet.getNextNewAccount(node, { 'foo': 'bar' })) + await assert.rejects(wallet.getNextNewAccount(rpc, { 'foo': 'bar' })) }) }) -describe.skip('refreshing wallet accounts', async () => { +skip('refreshing wallet accounts', async () => { test('should get balance, frontier, and representative for one account', async () => { - const accounts = await wallet.refresh(node) + const accounts = await wallet.refresh(rpc) const account = accounts[0] assert.ok(account instanceof Account) - assert.equal(typeof account.balance, 'bigint') + assert.equals(typeof account.balance, 'bigint') assert.notEqual(account.frontier, undefined) assert.notEqual(account.frontier, null) - assert.equal(typeof account.frontier, 'string') + assert.equals(typeof account.frontier, 'string') }) test('should get balance, frontier, and representative for multiple accounts', async () => { - const accounts = await wallet.refresh(node, 0, 2) - assert.equal(accounts.length, 1) + const accounts = await wallet.refresh(rpc, 0, 2) + assert.equals(accounts.length, 1) assert.ok(accounts[0] instanceof Account) }) test('should handle failure gracefully', async () => { - await assert.doesNotReject(wallet.refresh(node, 0, 20)) + await assert.doesNotReject(wallet.refresh(rpc, 0, 20)) }) }) diff --git a/test/sign-blocks.test.mjs b/test/sign-blocks.test.mjs index d37439c..0415a97 100644 --- a/test/sign-blocks.test.mjs +++ b/test/sign-blocks.test.mjs @@ -3,12 +3,11 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, test } from '#test/GLOBALS.mjs' import { NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { SendBlock, ReceiveBlock, ChangeBlock } from '#dist/main.js' -describe('valid blocks', async () => { +test('valid blocks', async () => { test('should not allow negative balances', async () => { assert.throws(() => { const block = new SendBlock( @@ -32,7 +31,7 @@ describe('valid blocks', async () => { '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D' ) assert.notEqual(block.balance, 0) - assert.equal(block.balance, BigInt(0)) + assert.equals(block.balance, BigInt(0)) }) test('should subtract balance from SendBlock correctly', async () => { @@ -44,7 +43,7 @@ describe('valid blocks', async () => { NANO_TEST_VECTORS.ADDRESS_2, '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D' ) - assert.equal(block.balance, 1000000000000000000000000000000n) + assert.equals(block.balance, 1000000000000000000000000000000n) }) test('should add balance from ReceiveBlock correctly', async () => { @@ -56,11 +55,11 @@ describe('valid blocks', async () => { NANO_TEST_VECTORS.ADDRESS_2, '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D' ) - assert.equal(block.balance, 3000000000000000000000000000000n) + assert.equals(block.balance, 3000000000000000000000000000000n) }) }) -describe('block signing tests using official test vectors', async () => { +test('block signing tests using official test vectors', async () => { test('should create a valid signature for an open block', async () => { const block = new ReceiveBlock( NANO_TEST_VECTORS.OPEN_BLOCK.account, @@ -72,8 +71,8 @@ describe('block signing tests using official test vectors', async () => { NANO_TEST_VECTORS.OPEN_BLOCK.work ) await block.sign(NANO_TEST_VECTORS.OPEN_BLOCK.key) - assert.equal(await block.hash(), NANO_TEST_VECTORS.OPEN_BLOCK.hash) - assert.equal(block.signature, NANO_TEST_VECTORS.OPEN_BLOCK.signature) + assert.equals(await block.hash(), NANO_TEST_VECTORS.OPEN_BLOCK.hash) + assert.equals(block.signature, NANO_TEST_VECTORS.OPEN_BLOCK.signature) }) test('should create a valid signature for a receive block', async () => { @@ -87,8 +86,8 @@ describe('block signing tests using official test vectors', async () => { NANO_TEST_VECTORS.RECEIVE_BLOCK.work ) await block.sign(NANO_TEST_VECTORS.RECEIVE_BLOCK.key) - assert.equal(await block.hash(), NANO_TEST_VECTORS.RECEIVE_BLOCK.hash) - assert.equal(block.signature, NANO_TEST_VECTORS.RECEIVE_BLOCK.signature) + assert.equals(await block.hash(), NANO_TEST_VECTORS.RECEIVE_BLOCK.hash) + assert.equals(block.signature, NANO_TEST_VECTORS.RECEIVE_BLOCK.signature) }) test('should create a valid signature for a receive block without work', async () => { @@ -101,9 +100,9 @@ describe('block signing tests using official test vectors', async () => { NANO_TEST_VECTORS.RECEIVE_BLOCK.previous ) await block.sign(NANO_TEST_VECTORS.RECEIVE_BLOCK.key) - assert.equal(await block.hash(), NANO_TEST_VECTORS.RECEIVE_BLOCK.hash) - assert.equal(block.signature, NANO_TEST_VECTORS.RECEIVE_BLOCK.signature) - assert.equal(block.work, '') + assert.equals(await block.hash(), NANO_TEST_VECTORS.RECEIVE_BLOCK.hash) + assert.equals(block.signature, NANO_TEST_VECTORS.RECEIVE_BLOCK.signature) + assert.equals(block.work, '') }) test('should create a valid signature for a send block', async () => { @@ -117,8 +116,8 @@ describe('block signing tests using official test vectors', async () => { NANO_TEST_VECTORS.SEND_BLOCK.work ) await block.sign(NANO_TEST_VECTORS.SEND_BLOCK.key) - assert.equal(await block.hash(), NANO_TEST_VECTORS.SEND_BLOCK.hash) - assert.equal(block.signature, NANO_TEST_VECTORS.SEND_BLOCK.signature) + assert.equals(await block.hash(), NANO_TEST_VECTORS.SEND_BLOCK.hash) + assert.equals(block.signature, NANO_TEST_VECTORS.SEND_BLOCK.signature) }) test('should create a valid signature for a send block without work', async () => { @@ -131,9 +130,9 @@ describe('block signing tests using official test vectors', async () => { NANO_TEST_VECTORS.SEND_BLOCK.previous ) await block.sign(NANO_TEST_VECTORS.SEND_BLOCK.key) - assert.equal(await block.hash(), NANO_TEST_VECTORS.SEND_BLOCK.hash) - assert.equal(block.signature, NANO_TEST_VECTORS.SEND_BLOCK.signature) - assert.equal(block.work, '') + assert.equals(await block.hash(), NANO_TEST_VECTORS.SEND_BLOCK.hash) + assert.equals(block.signature, NANO_TEST_VECTORS.SEND_BLOCK.signature) + assert.equals(block.work, '') }) test('should create a valid signature for a change rep block', async () => { @@ -146,8 +145,8 @@ describe('block signing tests using official test vectors', async () => { work, ) await block.sign('781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3') // Did not find a private key at nano docs for this address - assert.equal(block.signature?.toUpperCase(), 'A3C3C66D6519CBC0A198E56855942DEACC6EF741021A1B11279269ADC587DE1DA53CD478B8A47553231104CF24D742E1BB852B0546B87038C19BAE20F9082B0D') - assert.equal(block.work, work) + assert.equals(block.signature?.toUpperCase(), 'A3C3C66D6519CBC0A198E56855942DEACC6EF741021A1B11279269ADC587DE1DA53CD478B8A47553231104CF24D742E1BB852B0546B87038C19BAE20F9082B0D') + assert.equals(block.work, work) }) test('should create a valid signature for a change rep block without work', async () => { @@ -158,7 +157,7 @@ describe('block signing tests using official test vectors', async () => { 'F3C1D7B6EE97DA09D4C00538CEA93CBA5F74D78FD3FBE71347D2DFE7E53DF327' ) await block.sign(NANO_TEST_VECTORS.PRIVATE_0) - assert.equal(block.signature?.toUpperCase(), '2BD2F905E74B5BEE3E2277CED1D1E3F7535E5286B6E22F7B08A814AA9E5C4E1FEA69B61D60B435ADC2CE756E6EE5F5BE7EC691FE87E024A0B22A3D980CA5B305') - assert.equal(block.work, '') + assert.equals(block.signature?.toUpperCase(), '2BD2F905E74B5BEE3E2277CED1D1E3F7535E5286B6E22F7B08A814AA9E5C4E1FEA69B61D60B435ADC2CE756E6EE5F5BE7EC691FE87E024A0B22A3D980CA5B305') + assert.equals(block.work, '') }) }) diff --git a/test/tools.test.mjs b/test/tools.test.mjs index 165c9ed..04be748 100644 --- a/test/tools.test.mjs +++ b/test/tools.test.mjs @@ -3,54 +3,59 @@ 'use strict' -import '#test/GLOBALS.mjs' -import { describe, expect, test } from '@jest/globals' +import { assert, skip, test } from '#test/GLOBALS.mjs' import { RAW_MAX, NANO_TEST_VECTORS } from '#test/TEST_VECTORS.js' import { Bip44Wallet, Account, SendBlock, Rpc, Tools } from '#dist/main.js' const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(NANO_TEST_VECTORS.PASSWORD) -const rpc = new Rpc(process.env.NODE_URL ?? '', process.env.API_KEY_NAME) - -describe('unit conversion tests', async () => { +let rpc +//@ts-expect-error +var process = process || null +if (process) { + //@ts-expect-error + rpc = new Rpc(process?.env?.NODE_URL ?? '', process?.env?.API_KEY_NAME) +} + +test('unit conversion tests', async () => { test('should convert nano to raw', async () => { const result = await Tools.convert('1', 'NANO', 'RAW') - assert.equal(result, '1000000000000000000000000000000') + assert.equals(result, '1000000000000000000000000000000') }) test('should convert raw to nano', async () => { const result = await Tools.convert('1000000000000000000000000000000', 'RAW', 'NANO') - assert.equal(result, '1') + assert.equals(result, '1') }) test('should convert 1 raw to 10^-29 nano', async () => { const result = await Tools.convert('1', 'RAW', 'NANO') - assert.equal(result, '.000000000000000000000000000001') + assert.equals(result, '.000000000000000000000000000001') }) test('should ignore leading and trailing zeros', async () => { const result = await Tools.convert('0011002200.0033004400', 'nano', 'nano') - assert.equal(result, '11002200.00330044') + assert.equals(result, '11002200.00330044') }) test('should convert raw to nyano', async () => { const result = await Tools.convert(RAW_MAX, 'RAW', 'NYANO') - assert.equal(result, '340282366920938.463463374607431768211455') + assert.equals(result, '340282366920938.463463374607431768211455') }) test('should convert case-insensitive nyano to raw', async () => { const result = await Tools.convert('0.000000000000000123456789', 'nYaNo', 'rAw') - assert.equal(result, '123456789') + assert.equals(result, '123456789') }) test('should convert nano to pico', async () => { const result = await Tools.convert('123.456', 'nano', 'pico') - assert.equal(result, '123456') + assert.equals(result, '123456') }) test('should convert knano to pico', async () => { const result = await Tools.convert('123.456', 'nano', 'pico') - assert.equal(result, '123456') + assert.equals(result, '123456') }) test('should throw if amount exceeds raw max', async () => { @@ -74,26 +79,26 @@ describe('unit conversion tests', async () => { }) }) -describe('signature tests', async () => { +test('signature tests', async () => { test('should sign data with a single parameter', async () => { const result = await Tools.sign(NANO_TEST_VECTORS.PRIVATE_0, 'miro@metsanheimo.fi') - assert.equal(result, 'FECB9B084065ADC969904B55A0099C63746B68DF41FECB713244D387EED83A80B9D4907278C5EBC0998A5FC8BA597FBAAABBFCE0ABD2CA2212ACFE788637040C') + assert.equals(result, 'FECB9B084065ADC969904B55A0099C63746B68DF41FECB713244D387EED83A80B9D4907278C5EBC0998A5FC8BA597FBAAABBFCE0ABD2CA2212ACFE788637040C') }) test('should sign data with multiple parameters', async () => { const result = await Tools.sign(NANO_TEST_VECTORS.PRIVATE_0, 'miro@metsanheimo.fi', 'somePassword') - assert.equal(result, 'BB534F9B469AF451B1941FFEF8EE461FC5D284B5D393140900C6E13A65EF08D0AE2BC77131EE182922F66C250C7237A83878160457D5C39A70E55F7FCE925804') + assert.equals(result, 'BB534F9B469AF451B1941FFEF8EE461FC5D284B5D393140900C6E13A65EF08D0AE2BC77131EE182922F66C250C7237A83878160457D5C39A70E55F7FCE925804') }) test('should verify a signature using the public key', async () => { const result = await Tools.verify(NANO_TEST_VECTORS.PUBLIC_0, 'FECB9B084065ADC969904B55A0099C63746B68DF41FECB713244D387EED83A80B9D4907278C5EBC0998A5FC8BA597FBAAABBFCE0ABD2CA2212ACFE788637040C', 'miro@metsanheimo.fi') - assert.equal(result, true) + assert.equals(result, true) const result2 = await Tools.verify(NANO_TEST_VECTORS.PUBLIC_0, 'FECB9B084065ADC969904B55A0099C63746B68DF41FECB713244D387EED83A80B9D4907278C5EBC0998A5FC8BA597FBAAABBFCE0ABD2CA2212ACFE788637040C', 'mir@metsanheimo.fi') - assert.equal(result2, false) + assert.equals(result2, false) const result3 = await Tools.verify(NANO_TEST_VECTORS.PUBLIC_0, 'AECB9B084065ADC969904B55A0099C63746B68DF41FECB713244D387EED83A80B9D4907278C5EBC0998A5FC8BA597FBAAABBFCE0ABD2CA2212ACFE788637040C', 'miro@metsanheimo.fi') - assert.equal(result3, false) + assert.equals(result3, false) }) test('should verify a block using the public key', async () => { @@ -109,7 +114,7 @@ describe('signature tests', async () => { ) await sendBlock.sign(account.privateKey ?? '') const valid = await sendBlock.verify(account.publicKey) - assert.equal(valid, true) + assert.equals(valid, true) }) test('should reject a block using the wrong public key', async () => { @@ -127,20 +132,20 @@ describe('signature tests', async () => { sendBlock.account = Account.fromAddress('nano_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p') const valid = await sendBlock.verify(account.publicKey) - assert.equal(valid, false) + assert.equals(valid, false) }) }) -describe('sweeper', async () => { +test('sweeper', async () => { test('throws without required parameters', async () => { //@ts-expect-error await assert.rejects(Tools.sweep(), - { message: 'Missing required sweep arguments' }) + 'Missing required sweep arguments') }) - test.skip('fails gracefully for ineligible accounts', async () => { + skip('fails gracefully for ineligible accounts', async () => { const results = await Tools.sweep(rpc, wallet, NANO_TEST_VECTORS.ADDRESS_1) assert.ok(results) - assert.equal(results.length, 1) + assert.equals(results.length, 1) }) }) -- 2.34.1