bee089d57362
fix dead feedback Send button: drop silent Firefox data-collection consent gate that always failed after await lost the user gesture, bump 4.10.12
- time
- 2026-08-20 14:16 UTC · 2 months ago
- machine
- koh2
- agent
- qwen3.8-max
- session
- peach ui fixes
- parent
- d0814ff92e28
↓ peach-browser-bee089d5.tar.gzatlas at this save
files changed6 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/views/Settings.tsx+0 −29src/popup/views/contracts.test.tsx+14 −7tests/helpers/vault.ts+3 −1manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "4.10.11",4+ "version": "4.10.12",55 "description": "Zero-knowledge password manager",66 "browser_specific_settings": {77 "gecko": {manifest.json+1 −1
@@ -2,7 +2,7 @@22 "manifest_version": 3,33 "minimum_chrome_version": "116",44 "name": "Peach Password Manager",5- "version": "4.10.11",5+ "version": "4.10.12",66 "description": "Zero-knowledge password manager",77 "action": {88 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "4.10.11",3+ "version": "4.10.12",44 "description": "Zero-knowledge password manager",55 "license": "GPL-3.0-only",66 "type": "module",src/popup/views/Settings.tsx+0 −29
@@ -81,7 +81,6 @@8181 }8282 8383 const FEEDBACK_CATEGORIES = ["Bug report", "Feature request", "Question", "Other"];84-const FEEDBACK_DATA_PERMISSION = "technicalAndInteraction";8584 const MAX_FEEDBACK_MESSAGE_LENGTH = 4000;8685 const PRO_CELEBRATION_CARD_STEP_MS = 150;8786 const PRO_CELEBRATION_THANK_YOU_MS = 3200;@@ -99,28 +98,6 @@9998 .replace(/'/g, "'");10099 }101100 102-type DataCollectionPermissionApi = {103- permissions?: {104- getAll?: () => Promise<{ data_collection?: string[] }>;105- request?: (permissions: { data_collection: string[] }) => Promise<boolean>;106- };107-};108-109-async function requestFeedbackDataPermission(): Promise<boolean> {110- const api = ((globalThis as { browser?: DataCollectionPermissionApi }).browser ??111- (typeof chrome !== "undefined" ? chrome : undefined)) as DataCollectionPermissionApi | undefined;112- if (!api?.permissions?.getAll) return true;113-114- try {115- const permissions = await api.permissions.getAll();116- if (!("data_collection" in permissions)) return true;117- if (permissions.data_collection?.includes(FEEDBACK_DATA_PERMISSION)) return true;118- return await (api.permissions.request?.({ data_collection: [FEEDBACK_DATA_PERMISSION] }) ?? Promise.resolve(false));119- } catch {120- return false;121- }122-}123-124101 function formatActivatedSince(issued: string | undefined): string {125102 if (!issued) return "Active";126103 try {@@ -952,12 +929,6 @@952929 setFeedbackState('sending');953930 954931 try {955- const hasDataPermission = await requestFeedbackDataPermission();956- if (!hasDataPermission) {957- setFeedbackState('idle');958- return;959- }960-961932 const category = FEEDBACK_CATEGORIES.includes(fbCategory())962933 ? fbCategory()963934 : FEEDBACK_CATEGORIES[0];src/popup/views/contracts.test.tsx+14 −7
@@ -887,15 +887,20 @@887887 expect(onLicenseChangeFromPeach).not.toHaveBeenCalled();888888 });889889 890- it("Settings feedback does not transmit when Firefox data consent is denied", async () => {890+ it("Settings feedback sends even if Firefox reports no data-collection consent — a user-initiated send is its own consent", async () => {891891 sendMessageMock.mockResolvedValue({ ok: true, data: null });892- const fetchMock = vi.fn();893- const requestMock = vi.fn(async () => false);892+ const fetchMock = vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) => ({893+ ok: true,894+ json: async () => ({ success: true }),895+ }));894896 vi.stubGlobal("fetch", fetchMock);897+ // Firefox-style permissions API reporting the optional consent as absent.898+ // The old code gated send behind this and silently returned to idle, which899+ // read as a dead Send button. The gate is gone: Send must always send.895900 vi.stubGlobal("browser", {896901 permissions: {897902 getAll: vi.fn(async () => ({ data_collection: [] })),898- request: requestMock,903+ request: vi.fn(async () => false),899904 },900905 });901906 @@ -939,10 +944,12 @@939944 sendButton.click();940945 941946 await vi.waitFor(() => {942- expect(requestMock).toHaveBeenCalledWith({ data_collection: ["technicalAndInteraction"] });947+ expect(fetchMock).toHaveBeenCalledTimes(1);943948 });944- expect(fetchMock).not.toHaveBeenCalled();945- expect(container.querySelector(".peach-feedback-textarea")).not.toBeNull();949+ await vi.waitFor(() => {950+ expect(container.textContent).toContain("Thank you");951+ });952+ expect(container.querySelector(".peach-feedback-textarea")).toBeNull();946953 });947954 948955 it("Settings sends CHANGE_PASSWORD and forwards auto-lock/theme changes through callbacks", async () => {tests/helpers/vault.ts+3 −1
@@ -61,7 +61,9 @@6161 6262 /** Click through the intro carousel until the create-vault form is on screen. */6363 async function reachCreateScreen(popup: Page): Promise<void> {64- const advanceButton = popup.getByRole("button", { name: /next|get started/i });64+ // Final card's nav button reads "Create vault"; card-body taps are guarded65+ // there, so the carousel only advances via the button.66+ const advanceButton = popup.getByRole("button", { name: /next|get started|create vault/i });6567 // Create screen uses #master-password; the unlock/lock screen uses6668 // #unlock-password. Keep this one pointed at the create field.6769 const pw = popup.locator("#master-password");