// Every screen that asks for a password shows a password error the same way: // through showError() and hideError() in src/popup/views/helpers.js, in a // fixed-height error line below the password field, and never in the flash // line at the top of the popup // (https://git.eeqj.de/sneak/AutistMask/issues/493). These boot the real popup // over src/popup/index.html, so each error line has to exist in the markup, // and check that the error appears in it and clears again. jest.mock("../src/shared/vault", () => ({ decryptWithPassword: jest.fn(), encryptWithPassword: jest.fn(), })); const { bootPopup, cleanupPopup, unversionedValidProfile, } = require("./support/popupBoot"); const PASSWORD = "correct horse battery staple"; const WRONG_PASSWORD = "That password is incorrect. Please try again."; afterEach(() => { cleanupPopup(); }); // The error line as the user sees it. function errorLine(page, id) { return { inMarkup: page.document.authoredIds.has(id), text: page.text(id), visibility: page.node(id).style.visibility, }; } function shown(text) { return { inMarkup: true, text, visibility: "visible" }; } const cleared = { inMarkup: true, text: "", visibility: "hidden" }; describe("the add wallet screen", () => { const ERROR = "add-wallet-password-error"; // First run: Welcome, "Add wallet", then the die for a valid phrase. async function openAddWallet() { const page = await bootPopup(undefined); await page.click("btn-welcome-add"); await page.click("btn-generate-phrase"); return page; } function setPasswords(page, password, confirm) { page.node("add-wallet-password").value = password; page.node("add-wallet-password-confirm").value = confirm; } test("shows a password problem below the password fields, and clears it on the next press", async () => { const page = await openAddWallet(); setPasswords(page, "short", "short"); await page.click("btn-add-wallet-confirm"); expect(errorLine(page, ERROR)).toEqual( shown("Password must be at least 12 characters."), ); expect(page.text("flash-msg")).toBe(""); // The password is fixed and the phrase emptied: the password error // goes, and the phrase problem is still reported in the flash line. setPasswords(page, PASSWORD, PASSWORD); page.node("wallet-mnemonic").value = ""; await page.click("btn-add-wallet-confirm"); expect(errorLine(page, ERROR)).toEqual(cleared); expect(page.text("flash-msg")).toBe( "Enter a recovery phrase, or press the die.", ); // Leaving clears the flash line and stops its timer, which would // otherwise fire after this page is gone. await page.click("btn-add-wallet-back"); }); test("clears the error when the screen is shown again", async () => { const page = await openAddWallet(); setPasswords(page, PASSWORD, PASSWORD + " typo"); await page.click("btn-add-wallet-confirm"); expect(errorLine(page, ERROR)).toEqual( shown("Passwords do not match."), ); await page.click("btn-add-wallet-back"); await page.click("btn-welcome-add"); expect(errorLine(page, ERROR)).toEqual(cleared); }); }); describe.each([ { screen: "the private key export screen", open: () => require("../src/popup/views/exportPrivkey").show(0, 0), field: "export-privkey-password", button: "btn-export-privkey-confirm", error: "export-privkey-password-error", }, { screen: "the recovery phrase screen", open: () => require("../src/popup/views/showPhrase").show(0), field: "show-phrase-password", button: "btn-show-phrase-reveal", error: "show-phrase-password-error", }, { screen: "the delete wallet screen", open: () => require("../src/popup/views/deleteWallet").show(0), field: "delete-wallet-password", button: "btn-delete-wallet-confirm", error: "delete-wallet-password-error", }, ])("$screen", ({ open, field, button, error }) => { // Opens the screen and enters a password the vault rejects. async function failedAttempt() { const page = await bootPopup(unversionedValidProfile()); open(); const { decryptWithPassword } = require("../src/shared/vault"); decryptWithPassword.mockRejectedValue(new Error("wrong password")); page.node(field).value = "not the password"; await page.click(button); return { page, decryptWithPassword }; } test("shows a wrong password below the password field", async () => { const { page } = await failedAttempt(); expect(errorLine(page, error)).toEqual(shown(WRONG_PASSWORD)); }); test("clears the error while the next password is checked", async () => { const { page, decryptWithPassword } = await failedAttempt(); let rejectDecrypt; decryptWithPassword.mockReturnValue( new Promise((resolve, reject) => { rejectDecrypt = reject; }), ); page.node(field).value = "another guess"; const pressed = page.click(button); await page.settle(); expect(errorLine(page, error)).toEqual(cleared); rejectDecrypt(new Error("wrong password")); await pressed; expect(errorLine(page, error)).toEqual(shown(WRONG_PASSWORD)); }); test("clears the error when the screen is shown again", async () => { const { page } = await failedAttempt(); open(); expect(errorLine(page, error)).toEqual(cleared); }); });