import { describe, expect, it } from "vitest"; import { pmSave } from "../src/pm-save.js"; import type { PmPowers } from "../src/pm-save.js"; /** a fake console that records what was logged, by level */ function makeLog() { const entries: { level: string; args: unknown[] }[] = []; const at = (level: string) => (...args: unknown[]) => entries.push({ level, args }); return { log: { info: at("info"), warn: at("warn"), error: at("error") }, entries }; } class FakeCredential { constructor(readonly init: { id: string; password: string }) {} } function setup(store: (c: unknown) => Promise, petname = "my cache") { const { log, entries } = makeLog(); const stored: unknown[] = []; const powers: PmPowers = { petname: { value: petname }, credentials: { store: (c) => (stored.push(c), store(c)) }, PasswordCredential: FakeCredential, log, }; const btn = { textContent: "save to password manager", disabled: false }; return { powers, btn, entries, stored }; } describe("pmSave", () => { it("stores under the petname, and reports a spec-conformant resolve (undefined) as offered, not failed", async () => { const { powers, btn, entries, stored } = setup(() => Promise.resolve(undefined)); await pmSave(btn, "the-key", powers); expect(stored).toEqual([new FakeCredential({ id: "my cache", password: "the-key" })]); expect(btn.textContent).not.toMatch(/not saved|fail/); expect(btn.textContent).toMatch(/password manager/); expect(btn.disabled).toBe(false); expect(entries.map((e) => e.level)).toEqual(["info"]); }); it("logs, never silently, when the browser has no credential store", async () => { const { powers, btn, entries } = setup(() => Promise.resolve(undefined)); delete powers.PasswordCredential; await pmSave(btn, "the-key", powers); expect(btn.textContent).toMatch(/no credential store/); expect(entries.map((e) => e.level)).toEqual(["warn"]); }); it("logs the error when the store rejects, without the key", async () => { const { powers, btn, entries } = setup(() => Promise.reject(new Error("NotAllowedError"))); await pmSave(btn, "the-key", powers); expect(btn.textContent).toMatch(/save failed/); expect(entries.map((e) => e.level)).toEqual(["error"]); expect(JSON.stringify(entries)).not.toContain("the-key"); }); it("asks for a petname rather than saving under an empty username", async () => { const { powers, btn, stored } = setup(() => Promise.resolve(undefined), " "); await pmSave(btn, "the-key", powers); expect(stored).toEqual([]); expect(btn.textContent).toMatch(/petname/); }); });