// One wording for one condition (issue #172). // // Every screen that asks for the password decrypts the vault itself, and // each one used to write its own sentence for the same failure: the send // confirmation and the delete-wallet confirmation said "Wrong password." // (a fragment, which RULES.md Language & Labeling forbids), the reveal // screens said "That password is not correct.", and the two dApp approval // paths said "That password is incorrect." A user hitting two of those // minutes apart had no way to tell whether the wallet meant the same // thing. // // This scans the source rather than driving six views, because the // invariant is about the set of call sites and not about any one of them: // a seventh screen that decrypts the vault has to join the set, and a // DOM test per view cannot notice one that was never written. const fs = require("fs"); const path = require("path"); const SRC = path.join(__dirname, "..", "src"); const CANONICAL = "That password is incorrect. Please try again."; // Wordings this repo has actually shipped for the same condition. A // literal match, so a new variant is caught by the "only the canonical // sentence appears" assertion below rather than by guessing at phrasing. const SUPERSEDED = [ "Wrong password.", "That password is not correct. Please try again.", ]; function jsFilesUnder(dir) { return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { const full = path.join(dir, entry.name); if (entry.isDirectory()) return jsFilesUnder(full); return entry.name.endsWith(".js") ? [full] : []; }); } // The call sites are found, not listed: the file layout moves (the private // key export was in addressDetail.js when #172 was filed and is its own // view now), and a hardcoded list would quietly stop covering a screen it // no longer names. function callSites() { return jsFilesUnder(SRC).filter((file) => { if (file === path.join(SRC, "shared", "vault.js")) return false; return fs.readFileSync(file, "utf8").includes("decryptWithPassword("); }); } describe("password failure messages", () => { const files = callSites(); test("the call sites are found where they are expected", () => { const found = files .map((f) => path.relative(SRC, f).split(path.sep).join("/")) .sort(); expect(found).toEqual([ "popup/views/approval.js", "popup/views/confirmTx.js", "popup/views/deleteWallet.js", "popup/views/exportPrivkey.js", "popup/views/showPhrase.js", ]); }); test("the canonical message is a full sentence", () => { expect(CANONICAL).toMatch(/^[A-Z][^]*\.$/); }); test.each(files)("%s tells the user the canonical sentence", (f) => { expect(fs.readFileSync(f, "utf8")).toContain(CANONICAL); }); test.each(files)("%s carries no superseded wording", (f) => { const source = fs.readFileSync(f, "utf8"); for (const old of SUPERSEDED) expect(source).not.toContain(old); }); });