diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 208fbe08f..145e6efce 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -6,13 +6,14 @@ on: - master jobs: + test-node: runs-on: ubuntu-latest strategy: matrix: - node-version: [8.x, 10.x, 12.x, 13.x] + node-version: [8.x, 10.x, 12.x, 13.x, 14.x] steps: - name: Use Node.js ${{ matrix.node-version }} @@ -22,8 +23,10 @@ jobs: - uses: actions/checkout@v2 - run: npm ci - run: npm run bootstrap + - run: npm run build-all - run: npm run test-node + test-browser: runs-on: ubuntu-latest @@ -39,5 +42,22 @@ jobs: - uses: actions/checkout@v2 - run: npm ci - run: npm run bootstrap + - run: npm run build-all - run: npm run test-browser-${{ matrix.module }} + + coverage: + + name: Coverage + + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-node@v1 + with: + node-version: 12.x + - uses: actions/checkout@v2 + - run: npm ci + - run: npm run bootstrap + - run: npm run build-all + - run: npm run test-coverage diff --git a/package.json b/package.json index 42f82dc74..b673b30c5 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,11 @@ "_dist-test-umd": "rollup -c rollup-tests.config.js", "_test-browser-umd": "karma start --single-run --browsers ChromeHeadless karma-umd.conf.js", "_test-browser-esm": "karma start --single-run --browsers ChromeHeadless karma-esm.conf.js", - "_test-node": "mocha --no-colors --reporter ./packages/tests/reporter ./packages/tests/lib/test-*.js", - "test-browser-umd": "npm run build-all && npm run _dist-test-umd && npm run _test-browser-umd", - "test-browser-esm": "npm run build-all && npm run _dist-test-esm && npm run _test-browser-esm", - "test-node": "npm run build-all && npm run _test-node", + "test-browser-umd": "npm run _dist-test-umd && npm run _test-browser-umd", + "test-browser-esm": "npm run _dist-test-esm && npm run _test-browser-esm", + "test-node": "mocha --no-colors --reporter ./packages/tests/reporter ./packages/tests/lib/test-*.js", "test": "if [ \"$TEST\" == \"\" ]; then npm run test-node; else npm run \"test-$TEST\"; fi", + "test-coverage": "nyc mocha --reporter ./packages/tests/reporter-keepalive ./packages/tests/lib/test-*.js", "lock-versions": "node ./admin/cmds/lock-versions", "build-docs": "flatworm docs.wrm docs", "serve-docs": "node ./admin/cmds/serve-docs.js", @@ -39,6 +39,8 @@ "sync-github": "node ./admin/cmds/cache-github" }, "devDependencies": { + "@erquhart/rollup-plugin-node-builtins": "2.1.5", + "@istanbuljs/nyc-config-typescript": "^1.0.1", "@types/assert": "^1.4.1", "@types/mocha": "^5.2.0", "@types/node": "^12.7.4", @@ -53,10 +55,10 @@ "libnpmpublish": "1.1.3", "mocha": "^7.1.1", "npm-packlist": "1.4.1", + "nyc": "15.1.0", "rollup": "1.20.1", "rollup-plugin-commonjs": "10.0.2", "rollup-plugin-json": "4.0.0", - "@erquhart/rollup-plugin-node-builtins": "2.1.5", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-node-resolve": "5.2.0", "rollup-plugin-terser": "^5.2.0", @@ -64,7 +66,9 @@ "rollup-pluginutils": "2.8.1", "scrypt-js": "3.0.1", "semver": "^5.6.0", + "source-map-support": "^0.5.19", "tar": "4.4.8", + "ts-node": "^8.10.2", "typescript": "3.8.3" } } diff --git a/packages/cli/src.ts/cli.ts b/packages/cli/src.ts/cli.ts index 129f5ae96..5fc3a388f 100644 --- a/packages/cli/src.ts/cli.ts +++ b/packages/cli/src.ts/cli.ts @@ -1,3 +1,5 @@ +/* istanbul ignore file */ + "use strict"; import fs from "fs"; @@ -988,3 +990,4 @@ export class CLI { } } } + diff --git a/packages/cli/src.ts/prompt.ts b/packages/cli/src.ts/prompt.ts index b830fb2d1..87e4db179 100644 --- a/packages/cli/src.ts/prompt.ts +++ b/packages/cli/src.ts/prompt.ts @@ -1,3 +1,5 @@ +/* istanbul ignore file */ + "use strict"; export type PromptOptions = { diff --git a/packages/cli/src.ts/solc.ts b/packages/cli/src.ts/solc.ts index 5ed97697b..fbb3dde00 100644 --- a/packages/cli/src.ts/solc.ts +++ b/packages/cli/src.ts/solc.ts @@ -1,3 +1,5 @@ +/* istanbul ignore file */ + 'use strict'; import fs from "fs"; diff --git a/packages/cli/src.ts/typescript.ts b/packages/cli/src.ts/typescript.ts index c2615aca9..42a76fddb 100644 --- a/packages/cli/src.ts/typescript.ts +++ b/packages/cli/src.ts/typescript.ts @@ -1,3 +1,5 @@ +/* istanbul ignore file */ + "use strict"; import { ethers } from "ethers"; diff --git a/packages/hash/src.ts/index.ts b/packages/hash/src.ts/index.ts index 40ad878d1..407395c09 100644 --- a/packages/hash/src.ts/index.ts +++ b/packages/hash/src.ts/index.ts @@ -27,6 +27,7 @@ export function isValidName(name: string): boolean { } export function namehash(name: string): string { + /* istanbul ignore if */ if (typeof(name) !== "string") { logger.throwArgumentError("invalid address - " + String(name), "name", name); } diff --git a/packages/hdnode/src.ts/index.ts b/packages/hdnode/src.ts/index.ts index bef61c96e..8c94416a9 100644 --- a/packages/hdnode/src.ts/index.ts +++ b/packages/hdnode/src.ts/index.ts @@ -99,6 +99,7 @@ export class HDNode implements ExternallyOwnedAccount { constructor(constructorGuard: any, privateKey: string, publicKey: string, parentFingerprint: string, chainCode: string, index: number, depth: number, mnemonicOrPath: Mnemonic | string) { logger.checkNew(new.target, HDNode); + /* istanbul ignore if */ if (constructorGuard !== _constructorGuard) { throw new Error("HDNode constructor cannot be called directly"); } diff --git a/packages/sha2/src.ts/index.ts b/packages/sha2/src.ts/index.ts index 7c6733fbc..21aac5d82 100644 --- a/packages/sha2/src.ts/index.ts +++ b/packages/sha2/src.ts/index.ts @@ -25,6 +25,7 @@ export function sha512(data: BytesLike): string { export function computeHmac(algorithm: SupportedAlgorithm, key: BytesLike, data: BytesLike): string { + /* istanbul ignore if */ if (!SupportedAlgorithm[algorithm]) { logger.throwError("unsupported algorithm - " + algorithm, Logger.errors.UNSUPPORTED_OPERATION, { operation: "computeHmac", diff --git a/packages/tests/src.ts/reporter.ts b/packages/tests/src.ts/reporter.ts index 7e0e3cf6a..ab8046486 100644 --- a/packages/tests/src.ts/reporter.ts +++ b/packages/tests/src.ts/reporter.ts @@ -1,3 +1,5 @@ +/* istanbul ignore file */ + 'use strict'; // Maximum time in seconds to suppress output diff --git a/packages/tests/src.ts/utils.ts b/packages/tests/src.ts/utils.ts index f961e9d87..2f3bd85c5 100644 --- a/packages/tests/src.ts/utils.ts +++ b/packages/tests/src.ts/utils.ts @@ -1,3 +1,5 @@ +/* istanbul ignore file */ + 'use strict'; import { ethers } from "ethers"; diff --git a/packages/wallet/src.ts/index.ts b/packages/wallet/src.ts/index.ts index 1322f064a..66178344d 100644 --- a/packages/wallet/src.ts/index.ts +++ b/packages/wallet/src.ts/index.ts @@ -72,6 +72,7 @@ export class Wallet extends Signer implements ExternallyOwnedAccount { } else { if (SigningKey.isSigningKey(privateKey)) { + /* istanbul ignore if */ if (privateKey.curve !== "secp256k1") { logger.throwArgumentError("unsupported curve; must be secp256k1", "privateKey", "[REDACTED]"); } @@ -84,6 +85,7 @@ export class Wallet extends Signer implements ExternallyOwnedAccount { defineReadOnly(this, "address", computeAddress(this.publicKey)); } + /* istanbul ignore if */ if (provider && !Provider.isProvider(provider)) { logger.throwArgumentError("invalid provider", "provider", provider); } @@ -107,7 +109,7 @@ export class Wallet extends Signer implements ExternallyOwnedAccount { return resolveProperties(transaction).then((tx) => { if (tx.from != null) { if (getAddress(tx.from) !== this.address) { - throw new Error("transaction from address mismatch"); + logger.throwArgumentError("transaction from address mismatch", "transaction.from", transaction.from); } delete tx.from; } diff --git a/packages/web/src.ts/geturl.ts b/packages/web/src.ts/geturl.ts index ff2e9a752..b47f7411f 100644 --- a/packages/web/src.ts/geturl.ts +++ b/packages/web/src.ts/geturl.ts @@ -49,6 +49,7 @@ function getResponse(request: http.ClientRequest): Promise { }); resp.on("error", (error) => { + /* istanbul ignore next */ (error).response = response; reject(error); }); @@ -91,6 +92,7 @@ export async function getUrl(href: string, options?: Options): Promise = null; function loadWords(lang: Wordlist): void { if (wordlist != null) { return; } wordlist = words.replace(/([A-Z])/g, " $1").toLowerCase().substring(1).split(" "); + + // Verify the computed list matches the official list + /* istanbul ignore if */ if (Wordlist.check(lang) !== "0x25f44555f4af25b51a711136e1c7d6e50ce9f8917d39d6b1f076b2bb4d2fac1a") { wordlist = null; throw new Error("BIP39 Wordlist for en (English) FAILED"); diff --git a/packages/wordlists/src.ts/lang-en.ts b/packages/wordlists/src.ts/lang-en.ts index f19e258dd..308e11c73 100644 --- a/packages/wordlists/src.ts/lang-en.ts +++ b/packages/wordlists/src.ts/lang-en.ts @@ -11,6 +11,9 @@ let wordlist: Array = null; function loadWords(lang: Wordlist): void { if (wordlist != null) { return; } wordlist = words.replace(/([A-Z])/g, " $1").toLowerCase().substring(1).split(" "); + + // Verify the computed list matches the official list + /* istanbul ignore if */ if (Wordlist.check(lang) !== "0x3c8acc1e7b08d8e76f9fda015ef48dc8c710a73cb7e0f77b2c18a9b5a7adde60") { wordlist = null; throw new Error("BIP39 Wordlist for en (English) FAILED"); diff --git a/packages/wordlists/src.ts/lang-es.ts b/packages/wordlists/src.ts/lang-es.ts index bb21bd103..9a609f747 100644 --- a/packages/wordlists/src.ts/lang-es.ts +++ b/packages/wordlists/src.ts/lang-es.ts @@ -43,6 +43,9 @@ function loadWords(lang: Wordlist): void { wordlist.forEach((word, index) => { lookup[dropDiacritic(word)] = index; }); + + // Verify the computed list matches the official list + /* istanbul ignore if */ if (Wordlist.check(lang) !== "0xf74fb7092aeacdfbf8959557de22098da512207fb9f109cb526994938cf40300") { wordlist = null; throw new Error("BIP39 Wordlist for es (Spanish) FAILED"); @@ -61,9 +64,7 @@ class LangEs extends Wordlist { getWordIndex(word: string): number { loadWords(this); - const index = lookup[dropDiacritic(word)]; - if (typeof(index) !== "number") { return -1; } - return index; + return lookup[dropDiacritic(word)]; } } diff --git a/packages/wordlists/src.ts/lang-fr.ts b/packages/wordlists/src.ts/lang-fr.ts index 3f02c3051..54a50694d 100644 --- a/packages/wordlists/src.ts/lang-fr.ts +++ b/packages/wordlists/src.ts/lang-fr.ts @@ -45,6 +45,9 @@ function loadWords(lang: Wordlist): void { wordlist.forEach((word, index) => { lookup[dropDiacritic(word)] = index; }); + + // Verify the computed list matches the official list + /* istanbul ignore if */ if (Wordlist.check(lang) !== "0x51deb7ae009149dc61a6bd18a918eb7ac78d2775726c68e598b92d002519b045") { wordlist = null; throw new Error("BIP39 Wordlist for fr (French) FAILED"); @@ -63,9 +66,7 @@ class LangFr extends Wordlist { getWordIndex(word: string): number { loadWords(this); - const index = lookup[dropDiacritic(word)]; - if (typeof(index) !== "number") { return -1; } - return index; + return lookup[dropDiacritic(word)]; } } diff --git a/packages/wordlists/src.ts/lang-it.ts b/packages/wordlists/src.ts/lang-it.ts index 2d387dd74..489727c76 100644 --- a/packages/wordlists/src.ts/lang-it.ts +++ b/packages/wordlists/src.ts/lang-it.ts @@ -9,6 +9,9 @@ let wordlist: Array = null; function loadWords(lang: Wordlist): void { if (wordlist != null) { return; } wordlist = words.replace(/([A-Z])/g, " $1").toLowerCase().substring(1).split(" "); + + // Verify the computed list matches the official list + /* istanbul ignore if */ if (Wordlist.check(lang) !== "0x5c1362d88fd4cf614a96f3234941d29f7d37c08c5292fde03bf62c2db6ff7620") { wordlist = null; throw new Error("BIP39 Wordlist for it (Italian) FAILED"); diff --git a/packages/wordlists/src.ts/lang-ja.ts b/packages/wordlists/src.ts/lang-ja.ts index 8af8299df..331fe7801 100644 --- a/packages/wordlists/src.ts/lang-ja.ts +++ b/packages/wordlists/src.ts/lang-ja.ts @@ -104,12 +104,16 @@ function loadWords(lang: Wordlist) { // - kyoku // - kiyoku + // This should ignore "if", but that doesn't work here?? + /* istanbul ignore next */ if (hex(wordlist[442]) === KiYoKu && hex(wordlist[443]) === KyoKu) { const tmp = wordlist[442]; wordlist[442] = wordlist[443]; wordlist[443] = tmp; } + // Verify the computed list matches the official list + /* istanbul ignore if */ if (Wordlist.check(lang) !== "0xcb36b09e6baa935787fd762ce65e80b0c6a8dabdfbc3a7f86ac0e2c4fd111600") { wordlist = null; throw new Error("BIP39 Wordlist for ja (Japanese) FAILED"); diff --git a/packages/wordlists/src.ts/lang-ko.ts b/packages/wordlists/src.ts/lang-ko.ts index 0100bfd39..c71451834 100644 --- a/packages/wordlists/src.ts/lang-ko.ts +++ b/packages/wordlists/src.ts/lang-ko.ts @@ -48,6 +48,8 @@ function loadWords(lang: Wordlist): void { wordlist.sort(); + // Verify the computed list matches the official list + /* istanbul ignore if */ if (Wordlist.check(lang) !== "0xf9eddeace9c5d3da9c93cf7d3cd38f6a13ed3affb933259ae865714e8a3ae71a") { wordlist = null; throw new Error("BIP39 Wordlist for ko (Korean) FAILED"); diff --git a/packages/wordlists/src.ts/lang-zh.ts b/packages/wordlists/src.ts/lang-zh.ts index 630f0dd35..66138d8cd 100644 --- a/packages/wordlists/src.ts/lang-zh.ts +++ b/packages/wordlists/src.ts/lang-zh.ts @@ -47,6 +47,8 @@ function loadWords(lang: Wordlist) { wordlist[lang.locale].push(toUtf8String(bytes)); } + // Verify the computed list matches the official list + /* istanbul ignore if */ if (Wordlist.check(lang) !== Checks[lang.locale]) { wordlist[lang.locale] = null; throw new Error("BIP39 Wordlist for " + lang.locale + " (Chinese) FAILED"); diff --git a/packages/wordlists/src.ts/wordlist.ts b/packages/wordlists/src.ts/wordlist.ts index a4b2b422b..2d207ffc7 100644 --- a/packages/wordlists/src.ts/wordlist.ts +++ b/packages/wordlists/src.ts/wordlist.ts @@ -35,6 +35,7 @@ export abstract class Wordlist { const words = []; for (let i = 0; i < 2048; i++) { const word = wordlist.getWord(i); + /* istanbul ignore if */ if (i !== wordlist.getWordIndex(word)) { return "0x"; } words.push(word); } @@ -43,6 +44,8 @@ export abstract class Wordlist { static register(lang: Wordlist, name?: string): void { if (!name) { name = lang.locale; } + + /* istanbul ignore if */ if (exportWordlist) { try { const anyGlobal = (window as any)