/* Any copyright is dedicated to the Public Domain. https://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; describe("settings ai features / Smart Window memories", () => { let doc, win; beforeEach(async function setup() { await SpecialPowers.pushPrefEnv({ set: [ ["browser.preferences.aiControls", true], ["browser.smartwindow.enabled", true], ["browser.smartwindow.tos.consentTime", 1770830464], ], }); await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true }); doc = gBrowser.selectedBrowser.contentDocument; win = doc.ownerGlobal; }); afterEach(() => { BrowserTestUtils.removeTab(gBrowser.selectedTab); }); async function openAiFeaturePanel() { const paneLoaded = waitForPaneChange("ai"); const categoryButton = doc.getElementById("category-ai-features"); categoryButton.scrollIntoView(); EventUtils.synthesizeMouseAtCenter(categoryButton, {}, win); await paneLoaded; } async function openSmartWindowPanel() { await openAiFeaturePanel(); const personalizeButton = doc.getElementById( "personalizeSmartWindowButton" ); personalizeButton.scrollIntoView(); const paneLoaded = waitForPaneChange("personalizeSmartWindow"); EventUtils.synthesizeMouseAtCenter(personalizeButton, {}, win); await paneLoaded; } async function openManageMemoriesPanel() { await openSmartWindowPanel(); const manageButton = doc.getElementById("manageMemoriesButton"); manageButton.scrollIntoView(); const paneLoaded = waitForPaneChange("manageMemories"); EventUtils.synthesizeMouseAtCenter(manageButton, {}, win); await paneLoaded; } async function populateMemories() { const { MemoryStore } = ChromeUtils.importESModule( "moz-src:///browser/components/aiwindow/services/MemoryStore.sys.mjs" ); let memoryOne = await MemoryStore.addMemory({ memory_summary: "Lorem ipsum dolor sit amet 1", category: "interests", intent: "general", score: 5, }); let memoryTwo = await MemoryStore.addMemory({ memory_summary: "Lorem ipsum dolor sit amet 2", category: "habits", intent: "general", score: 4, }); registerCleanupFunction(async () => { for (const { id } of [memoryOne, memoryTwo]) { try { await MemoryStore.hardDeleteMemory(id); } catch (err) { console.error("Failed to delete memory:", id, err); } } }); return { MemoryStore, memories: [memoryOne, memoryTwo] }; } it("toggles chat and browsing memory controls and shows correct empty states", async () => { await SpecialPowers.pushPrefEnv({ set: [ ["browser.smartwindow.memories.generateFromConversation", false], ["browser.smartwindow.memories.generateFromHistory", false], ], }); await openSmartWindowPanel(); const chatCheckbox = doc.getElementById("learnFromChatActivity"); Assert.ok(!chatCheckbox.checked, "Chat checkbox is unchecked initially"); chatCheckbox.scrollIntoView(); EventUtils.synthesizeMouseAtCenter(chatCheckbox.labelEl, {}, win); await chatCheckbox.updateComplete; Assert.ok( Services.prefs.getBoolPref( "browser.smartwindow.memories.generateFromConversation" ), "Chat preference is now true" ); Assert.ok(chatCheckbox.checked, "Chat checkbox is now checked"); const browsingCheckbox = doc.getElementById("learnFromBrowsingActivity"); Assert.ok( !browsingCheckbox.checked, "Browsing checkbox is unchecked initially" ); browsingCheckbox.scrollIntoView(); EventUtils.synthesizeMouseAtCenter(browsingCheckbox.labelEl, {}, win); await browsingCheckbox.updateComplete; Assert.ok( Services.prefs.getBoolPref( "browser.smartwindow.memories.generateFromHistory" ), "Browsing preference is now true" ); Assert.ok(browsingCheckbox.checked, "Browsing checkbox is now checked"); // Verify manage memories pane shows learning-off empty state when both // memory generation prefs are disabled. const manageButton = doc.getElementById("manageMemoriesButton"); manageButton.scrollIntoView(); const paneLoaded = waitForPaneChange("manageMemories"); EventUtils.synthesizeMouseAtCenter(manageButton, {}, win); await paneLoaded; let noMemoriesItem = doc.getElementById("no-memories-stored"); Assert.ok(noMemoriesItem, "No memories item exists"); Assert.ok( BrowserTestUtils.isVisible(noMemoriesItem), "No memories item is visible" ); await SpecialPowers.pushPrefEnv({ set: [ ["browser.smartwindow.memories.generateFromConversation", false], ["browser.smartwindow.memories.generateFromHistory", false], ], }); const memoriesList = doc.getElementById("memoriesList"); if (memoriesList?.setting?.config?.asyncSetting) { memoriesList.setting.config.asyncSetting.emitChange(); } await TestUtils.waitForTick(); noMemoriesItem = doc.getElementById("no-memories-stored"); Assert.ok(noMemoriesItem, "No memories item exists"); Assert.equal( noMemoriesItem.dataset.l10nId, "ai-window-no-memories-learning-off", "Shows learning-off empty state l10n ID" ); }); it("renders and deletes memory items", async () => { await SpecialPowers.pushPrefEnv({ set: [ ["browser.smartwindow.memories.generateFromConversation", true], ["browser.smartwindow.memories.generateFromHistory", true], ], }); const { MemoryStore, memories } = await populateMemories(); const testMemory = memories[0]; await openManageMemoriesPanel(); const memoriesList = doc.getElementById("memoriesList"); await memoriesList.updateComplete; const memoryItems = memoriesList.querySelectorAll("[id^='memory-item']"); Assert.greaterOrEqual( memoryItems.length, 2, "At least two memory items are rendered" ); const initialMemories = await MemoryStore.getMemories(); const initialCount = initialMemories.length; const deleteButton = memoriesList.querySelector( `[memoryId="${testMemory.id}"][action="delete"]` ); Assert.ok(deleteButton, "Delete button exists for the memory"); EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win); await TestUtils.waitForCondition(async () => { const currentMemories = await MemoryStore.getMemories(); return currentMemories.length < initialCount; }, "Waiting for memory to be deleted"); const remainingMemories = await MemoryStore.getMemories(); Assert.ok( !remainingMemories.find(m => m.id === testMemory.id), "Memory was deleted" ); }); });