summaryrefslogtreecommitdiffstats
path: root/browser/components/firefoxview/tests/browser/browser_recently_closed_tabs.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/firefoxview/tests/browser/browser_recently_closed_tabs.js')
-rw-r--r--browser/components/firefoxview/tests/browser/browser_recently_closed_tabs.js886
1 files changed, 886 insertions, 0 deletions
diff --git a/browser/components/firefoxview/tests/browser/browser_recently_closed_tabs.js b/browser/components/firefoxview/tests/browser/browser_recently_closed_tabs.js
new file mode 100644
index 0000000000..22889a43eb
--- /dev/null
+++ b/browser/components/firefoxview/tests/browser/browser_recently_closed_tabs.js
@@ -0,0 +1,886 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(10);
+
+/**
+ * The recently closed tab list is populated on a per-window basis.
+ *
+ * By default, the withFirefoxView helper opens fx view in the current window.
+ * This ensures that the add_new_tab, close_tab,
+ * and open_then_close functions are creating sessionstore entries
+ * associated with the correct window where the tests are run.
+ */
+
+ChromeUtils.defineESModuleGetters(globalThis, {
+ SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
+});
+
+const RECENTLY_CLOSED_EVENT = [
+ ["firefoxview", "entered", "firefoxview", undefined],
+ ["firefoxview", "recently_closed", "tabs", undefined],
+];
+
+const CLOSED_TABS_OPEN_EVENT = [
+ ["firefoxview", "closed_tabs_open", "tabs", "false"],
+];
+
+const RECENTLY_CLOSED_DISMISS_EVENT = [
+ ["firefoxview", "dismiss_closed_tab", "tabs", undefined],
+];
+
+async function add_new_tab(URL) {
+ let tab = BrowserTestUtils.addTab(gBrowser, URL);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ return tab;
+}
+
+async function close_tab(tab) {
+ const sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
+ BrowserTestUtils.removeTab(tab);
+ await sessionStorePromise;
+}
+
+async function dismiss_tab(tab, content) {
+ info(`Dismissing tab ${tab.dataset.targeturi}`);
+ const closedObjectsChanged = () =>
+ TestUtils.topicObserved("sessionstore-closed-objects-changed");
+ let dismissButton = tab.querySelector(".closed-tab-li-dismiss");
+ EventUtils.synthesizeMouseAtCenter(dismissButton, {}, content);
+ await closedObjectsChanged();
+}
+
+add_setup(async function setup() {
+ // set updateTimeMs to 0 to prevent unexpected/unrelated DOM mutations during testing
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.tabs.firefox-view.updateTimeMs", 100000]],
+ });
+});
+
+add_task(async function test_empty_list() {
+ clearHistory();
+
+ await withFirefoxView({}, async browser => {
+ const { document } = browser.contentWindow;
+ let container = document.querySelector("#collapsible-tabs-container");
+ ok(
+ container.classList.contains("empty-container"),
+ "collapsible container should have correct styling when the list is empty"
+ );
+
+ Assert.ok(
+ document.getElementById("recently-closed-tabs-placeholder"),
+ "The empty message is displayed."
+ );
+
+ Assert.ok(
+ !document.querySelector("ol.closed-tabs-list"),
+ "The recently closed tabs list is not displayed."
+ );
+
+ const tab1 = await add_new_tab(URLs[0]);
+
+ await close_tab(tab1);
+
+ // The UI update happens asynchronously as we learn of the new closed tab.
+ await BrowserTestUtils.waitForMutationCondition(
+ container,
+ { attributeFilter: ["class"] },
+ () => !container.classList.contains("empty-container")
+ );
+ ok(
+ !container.classList.contains("empty-container"),
+ "collapsible container should have correct styling when the list is not empty"
+ );
+
+ Assert.ok(
+ !document.getElementById("recently-closed-tabs-placeholder"),
+ "The empty message is not displayed."
+ );
+
+ Assert.ok(
+ document.querySelector("ol.closed-tabs-list"),
+ "The recently closed tabs list is displayed."
+ );
+
+ is(
+ document.querySelector("ol.closed-tabs-list").children.length,
+ 1,
+ "recently-closed-tabs-list should have one list item"
+ );
+ });
+});
+
+add_task(async function test_list_ordering() {
+ Services.obs.notifyObservers(null, "browser:purge-session-history");
+ is(
+ SessionStore.getClosedTabCountForWindow(window),
+ 0,
+ "Closed tab count after purging session history"
+ );
+ await clearAllParentTelemetryEvents();
+
+ const closedObjectsChanged = () =>
+ TestUtils.topicObserved("sessionstore-closed-objects-changed");
+
+ const tab1 = await add_new_tab(URLs[0]);
+ const tab2 = await add_new_tab(URLs[1]);
+ const tab3 = await add_new_tab(URLs[2]);
+
+ gBrowser.selectedTab = tab3;
+
+ await close_tab(tab3);
+ await closedObjectsChanged();
+
+ await close_tab(tab2);
+ await closedObjectsChanged();
+
+ await close_tab(tab1);
+ await closedObjectsChanged();
+
+ await withFirefoxView({}, async browser => {
+ const { document } = browser.contentWindow;
+ const tabsList = document.querySelector("ol.closed-tabs-list");
+ await BrowserTestUtils.waitForMutationCondition(
+ tabsList,
+ { childList: true },
+ () => tabsList.children.length > 1
+ );
+
+ is(
+ document.querySelector("ol.closed-tabs-list").children.length,
+ 3,
+ "recently-closed-tabs-list should have three list items"
+ );
+
+ // check that the ordering is correct when user navigates to another tab, and then closes multiple tabs.
+ ok(
+ document
+ .querySelector("ol.closed-tabs-list")
+ .children[0].textContent.includes("mochi.test"),
+ "first list item in recently-closed-tabs-list is in the correct order"
+ );
+
+ ok(
+ document
+ .querySelector("ol.closed-tabs-list")
+ .children[2].textContent.includes("example.net"),
+ "last list item in recently-closed-tabs-list is in the correct order"
+ );
+
+ let ele = document.querySelector("ol.closed-tabs-list").firstElementChild;
+ let uri = ele.getAttribute("data-targeturi");
+ let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, uri);
+ ele.querySelector(".closed-tab-li-main").click();
+ await newTabPromise;
+
+ await TestUtils.waitForCondition(
+ () => {
+ let events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
+ false
+ ).parent;
+ return events && events.length >= 2;
+ },
+ "Waiting for entered and recently_closed firefoxview telemetry events.",
+ 200,
+ 100
+ );
+
+ TelemetryTestUtils.assertEvents(
+ RECENTLY_CLOSED_EVENT,
+ { category: "firefoxview" },
+ { clear: true, process: "parent" }
+ );
+
+ gBrowser.removeTab(gBrowser.selectedTab);
+
+ await clearAllParentTelemetryEvents();
+
+ await waitForElementVisible(
+ browser,
+ "#recently-closed-tabs-container > summary"
+ );
+ document.querySelector("#recently-closed-tabs-container > summary").click();
+
+ await TestUtils.waitForCondition(
+ () => {
+ let events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
+ false
+ ).parent;
+ return events && events.length >= 1;
+ },
+ "Waiting for closed_tabs_open firefoxview telemetry event.",
+ 200,
+ 100
+ );
+
+ TelemetryTestUtils.assertEvents(
+ CLOSED_TABS_OPEN_EVENT,
+ { category: "firefoxview" },
+ { clear: true, process: "parent" }
+ );
+ });
+});
+
+add_task(async function test_max_list_items() {
+ Services.obs.notifyObservers(null, "browser:purge-session-history");
+ is(
+ SessionStore.getClosedTabCountForWindow(window),
+ 0,
+ "Closed tab count after purging session history"
+ );
+
+ await open_then_close(URLs[0]);
+ await open_then_close(URLs[1]);
+ await open_then_close(URLs[2]);
+
+ // Seed the closed tabs count. We've assured that we've opened and
+ // closed at least three tabs because of the calls to open_then_close
+ // above.
+ let mockMaxTabsLength = 3;
+
+ await withFirefoxView({}, async browser => {
+ const { document } = browser.contentWindow;
+
+ // override this value for testing purposes
+ document.querySelector("recently-closed-tabs-list").maxTabsLength =
+ mockMaxTabsLength;
+
+ ok(
+ !document
+ .querySelector("#collapsible-tabs-container")
+ .classList.contains("empty-container"),
+ "collapsible container should have correct styling when the list is not empty"
+ );
+
+ Assert.ok(
+ !document.getElementById("recently-closed-tabs-placeholder"),
+ "The empty message is not displayed."
+ );
+
+ Assert.ok(
+ document.querySelector("ol.closed-tabs-list"),
+ "The recently closed tabs list is displayed."
+ );
+
+ is(
+ document.querySelector("ol.closed-tabs-list").children.length,
+ mockMaxTabsLength,
+ `recently-closed-tabs-list should have ${mockMaxTabsLength} list items`
+ );
+
+ const closedObjectsChanged = TestUtils.topicObserved(
+ "sessionstore-closed-objects-changed"
+ );
+ // add another tab
+ const tab = await add_new_tab(URLs[3]);
+ await close_tab(tab);
+ await closedObjectsChanged;
+ let firstListItem = document.querySelector("ol.closed-tabs-list")
+ .children[0];
+ await BrowserTestUtils.waitForMutationCondition(
+ firstListItem,
+ { characterData: true, childList: true, subtree: true },
+ () => firstListItem.textContent.includes(".org")
+ );
+ ok(
+ firstListItem.textContent.includes("example.org"),
+ "first list item in recently-closed-tabs-list should have been updated"
+ );
+
+ is(
+ document.querySelector("ol.closed-tabs-list").children.length,
+ mockMaxTabsLength,
+ `recently-closed-tabs-list should still have ${mockMaxTabsLength} list items`
+ );
+ });
+});
+
+add_task(async function test_time_updates_correctly() {
+ clearHistory();
+ is(
+ SessionStore.getClosedTabCountForWindow(window),
+ 0,
+ "Closed tab count after purging session history"
+ );
+
+ // Set the closed tabs state to include one tab that was closed 2 seconds ago.
+ // This is well below the initial threshold for displaying the 'Just now' timestamp.
+ // It is also much greater than the 5ms threshold we use for the updated pref value,
+ // which results in the timestamp text changing after the pref value is changed.
+ const TAB_CLOSED_AGO_MS = 2000;
+ const TAB_UPDATE_TIME_MS = 5;
+ const TAB_CLOSED_STATE = {
+ windows: [
+ {
+ tabs: [{ entries: [] }],
+ _closedTabs: [
+ {
+ state: { entries: [{ url: "https://www.example.com/" }] },
+ closedId: 0,
+ closedAt: Date.now() - TAB_CLOSED_AGO_MS,
+ image: null,
+ title: "Example",
+ },
+ ],
+ },
+ ],
+ };
+ await SessionStore.setBrowserState(JSON.stringify(TAB_CLOSED_STATE));
+
+ is(
+ SessionStore.getClosedTabCountForWindow(window),
+ 1,
+ "Closed tab count after setting browser state"
+ );
+
+ await withFirefoxView({}, async browser => {
+ const { document } = browser.contentWindow;
+
+ const tabsList = document.querySelector("ol.closed-tabs-list");
+ const numOfListItems = tabsList.children.length;
+ const lastListItem = tabsList.children[numOfListItems - 1];
+ const timeLabel = lastListItem.querySelector("span.closed-tab-li-time");
+ let initialTimeText = timeLabel.textContent;
+ Assert.stringContains(
+ initialTimeText,
+ "Just now",
+ "recently-closed-tabs list item time is 'Just now'"
+ );
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.tabs.firefox-view.updateTimeMs", TAB_UPDATE_TIME_MS]],
+ });
+
+ await BrowserTestUtils.waitForMutationCondition(
+ timeLabel,
+ { childList: true },
+ () => !timeLabel.textContent.includes("now")
+ );
+
+ isnot(
+ timeLabel.textContent,
+ initialTimeText,
+ "recently-closed-tabs list item time has updated"
+ );
+
+ await SpecialPowers.popPrefEnv();
+ });
+ // Cleanup recently closed tab data.
+ clearHistory();
+});
+
+add_task(async function test_list_maintains_focus_when_restoring_tab() {
+ await SpecialPowers.clearUserPref(RECENTLY_CLOSED_STATE_PREF);
+ Services.obs.notifyObservers(null, "browser:purge-session-history");
+ is(
+ SessionStore.getClosedTabCountForWindow(window),
+ 0,
+ "Closed tab count after purging session history"
+ );
+
+ const sandbox = sinon.createSandbox();
+ let setupCompleteStub = sandbox.stub(
+ TabsSetupFlowManager,
+ "isTabSyncSetupComplete"
+ );
+ setupCompleteStub.returns(true);
+
+ await open_then_close(URLs[0]);
+ await open_then_close(URLs[1]);
+ await open_then_close(URLs[2]);
+
+ await withFirefoxView({}, async browser => {
+ let gBrowser = browser.getTabBrowser();
+ const { document } = browser.contentWindow;
+ const list = document.querySelectorAll(".closed-tab-li");
+ list[0].querySelector(".closed-tab-li-main").focus();
+ EventUtils.synthesizeKey("KEY_Enter");
+ let firefoxViewTab = gBrowser.tabs.find(tab => tab.label == "Firefox View");
+ await BrowserTestUtils.switchTab(gBrowser, firefoxViewTab);
+ Assert.ok(
+ document.activeElement.textContent.includes("mochitest index"),
+ "Focus should be on the first item in the recently closed list"
+ );
+ });
+
+ // clean up extra tabs
+ while (gBrowser.tabs.length > 1) {
+ BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
+ }
+
+ clearHistory();
+ await open_then_close(URLs[2]);
+ await withFirefoxView({}, async browser => {
+ let gBrowser = browser.getTabBrowser();
+ const { document } = browser.contentWindow;
+ let expectedFocusedElement = document.getElementById(
+ "recently-closed-tabs-header-section"
+ );
+ const list = document.querySelectorAll(".closed-tab-li");
+ list[0].querySelector(".closed-tab-li-main").focus();
+ EventUtils.synthesizeKey("KEY_Enter");
+ let firefoxViewTab = gBrowser.tabs.find(tab => tab.label == "Firefox View");
+ await BrowserTestUtils.switchTab(gBrowser, firefoxViewTab);
+ is(
+ document.activeElement,
+ expectedFocusedElement,
+ "Focus should be on the section header"
+ );
+ });
+
+ // clean up extra tabs
+ while (gBrowser.tabs.length > 1) {
+ BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
+ }
+});
+
+add_task(async function test_switch_before_closing() {
+ clearHistory();
+
+ const INITIAL_URL = "https://example.org/iwilldisappear";
+ const FINAL_URL = "https://example.com/ishouldappear";
+ await withFirefoxView({}, async function (browser) {
+ let gBrowser = browser.getTabBrowser();
+ let newTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ INITIAL_URL
+ );
+ // Switch back to FxView:
+ await BrowserTestUtils.switchTab(
+ gBrowser,
+ gBrowser.getTabForBrowser(browser)
+ );
+ // Update the tab we opened to a different site:
+ let loadPromise = BrowserTestUtils.browserLoaded(
+ newTab.linkedBrowser,
+ null,
+ FINAL_URL
+ );
+ BrowserTestUtils.loadURIString(newTab.linkedBrowser, FINAL_URL);
+ await loadPromise;
+ // Close the added tab
+ BrowserTestUtils.removeTab(newTab);
+
+ const { document } = browser.contentWindow;
+ await BrowserTestUtils.waitForMutationCondition(
+ document.querySelector("recently-closed-tabs-list"),
+ { childList: true, subtree: true },
+ () => document.querySelector("ol.closed-tabs-list")
+ );
+ const tabsList = document.querySelector("ol.closed-tabs-list");
+ info("A tab appeared in the list, ensure it has the right URL.");
+ let urlBit = tabsList.children[0].querySelector(".closed-tab-li-url");
+ await BrowserTestUtils.waitForMutationCondition(
+ urlBit,
+ { characterData: true, attributeFilter: ["title"] },
+ () => urlBit.textContent.includes(".com")
+ );
+ Assert.ok(
+ urlBit.textContent.includes("example.com"),
+ "Item should end up with the correct URL."
+ );
+ });
+});
+
+add_task(async function test_alt_click_no_launch() {
+ Services.obs.notifyObservers(null, "browser:purge-session-history");
+ is(
+ SessionStore.getClosedTabCountForWindow(window),
+ 0,
+ "Closed tab count after purging session history"
+ );
+
+ await open_then_close(URLs[0]);
+
+ await withFirefoxView({}, async browser => {
+ let gBrowser = browser.getTabBrowser();
+ let originalTabsLength = gBrowser.tabs.length;
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ ".closed-tab-li .closed-tab-li-main",
+ { altKey: true },
+ browser
+ );
+
+ is(
+ gBrowser.tabs.length,
+ originalTabsLength,
+ `Opened tabs length should still be ${originalTabsLength}`
+ );
+ });
+});
+
+/**
+ * Asserts that tabs that have been recently closed can be
+ * restored by clicking on them, using the Enter key,
+ * and using the Space bar.
+ */
+add_task(async function test_restore_recently_closed_tabs() {
+ clearHistory();
+
+ await open_then_close(URLs[0]);
+ await open_then_close(URLs[1]);
+ await open_then_close(URLs[2]);
+
+ await EventUtils.synthesizeMouseAtCenter(
+ gBrowser.ownerDocument.getElementById("firefox-view-button"),
+ { type: "mousedown" },
+ window
+ );
+ // Wait for Firefox View to be loaded before interacting
+ // with the page.
+ await BrowserTestUtils.browserLoaded(
+ window.FirefoxViewHandler.tab.linkedBrowser
+ );
+ let { document } = gBrowser.contentWindow;
+ let tabRestored = BrowserTestUtils.waitForNewTab(gBrowser, URLs[2]);
+ EventUtils.synthesizeMouseAtCenter(
+ document.querySelector(".closed-tab-li"),
+ {},
+ gBrowser.contentWindow
+ );
+
+ await tabRestored;
+ ok(true, "Tab was restored by mouse click");
+
+ await EventUtils.synthesizeMouseAtCenter(
+ gBrowser.ownerDocument.getElementById("firefox-view-button"),
+ { type: "mousedown" },
+ window
+ );
+
+ tabRestored = BrowserTestUtils.waitForNewTab(gBrowser, URLs[1]);
+ document.querySelector(".closed-tab-li .closed-tab-li-main").focus();
+ EventUtils.synthesizeKey("KEY_Enter", {}, gBrowser.contentWindow);
+
+ await tabRestored;
+ ok(true, "Tab was restored by using the Enter key");
+
+ // clean up extra tabs
+ while (gBrowser.tabs.length > 1) {
+ BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
+ }
+});
+
+/**
+ * Asserts that tabs are removed from Recently Closed tabs in
+ * Fx View when tabs are removed from latest closed tab data.
+ * Ex: Selecting "Reopen Closed Tab" from the tabs toolbar
+ * context menu
+ */
+add_task(async function test_reopen_recently_closed_tabs() {
+ clearHistory();
+
+ await open_then_close(URLs[0]);
+ await open_then_close(URLs[1]);
+ await open_then_close(URLs[2]);
+
+ await EventUtils.synthesizeMouseAtCenter(
+ gBrowser.ownerDocument.getElementById("firefox-view-button"),
+ { type: "mousedown" },
+ window
+ );
+ // Wait for Firefox View to be loaded before interacting
+ // with the page.
+ await BrowserTestUtils.browserLoaded(
+ window.FirefoxViewHandler.tab.linkedBrowser
+ );
+
+ let { document } = gBrowser.contentWindow;
+
+ let tabReopened = BrowserTestUtils.waitForNewTab(gBrowser, URLs[2]);
+ SessionStore.undoCloseTab(window);
+ await tabReopened;
+
+ const tabsList = document.querySelector("ol.closed-tabs-list");
+
+ await EventUtils.synthesizeMouseAtCenter(
+ gBrowser.ownerDocument.getElementById("firefox-view-button"),
+ { type: "mousedown" },
+ window
+ );
+
+ await BrowserTestUtils.waitForMutationCondition(
+ tabsList,
+ { childList: true },
+ () => tabsList.children.length === 2
+ );
+
+ Assert.equal(
+ tabsList.children[0].dataset.targeturi,
+ URLs[1],
+ `First recently closed item should be ${URLs[1]}`
+ );
+
+ await close_tab(gBrowser.visibleTabs[gBrowser.visibleTabs.length - 1]);
+
+ await BrowserTestUtils.waitForMutationCondition(
+ tabsList,
+ { childList: true },
+ () => tabsList.children.length === 3
+ );
+
+ Assert.equal(
+ tabsList.children[0].dataset.targeturi,
+ URLs[2],
+ `First recently closed item should be ${URLs[2]}`
+ );
+
+ await dismiss_tab(tabsList.children[0], content);
+
+ await BrowserTestUtils.waitForMutationCondition(
+ tabsList,
+ { childList: true },
+ () => tabsList.children.length === 2
+ );
+
+ Assert.equal(
+ tabsList.children[0].dataset.targeturi,
+ URLs[1],
+ `First recently closed item should be ${URLs[1]}`
+ );
+
+ const contextMenu = gBrowser.ownerDocument.getElementById(
+ "contentAreaContextMenu"
+ );
+ const promisePopup = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(
+ tabsList.querySelector(".closed-tab-li-title"),
+ {
+ button: 2,
+ type: "contextmenu",
+ },
+ gBrowser.contentWindow
+ );
+ await promisePopup;
+ const promiseNewTab = BrowserTestUtils.waitForNewTab(gBrowser, URLs[1]);
+ contextMenu.activateItem(
+ gBrowser.ownerDocument.getElementById("context-openlinkintab")
+ );
+ await promiseNewTab;
+
+ await BrowserTestUtils.waitForMutationCondition(
+ tabsList,
+ { childList: true },
+ () => tabsList.children.length === 1
+ );
+
+ Assert.equal(
+ tabsList.children[0].dataset.targeturi,
+ URLs[0],
+ `First recently closed item should be ${URLs[0]}`
+ );
+
+ // clean up extra tabs
+ while (gBrowser.tabs.length > 1) {
+ BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
+ }
+});
+
+/**
+ * Asserts that tabs that have been recently closed can be
+ * dismissed by clicking on their respective dismiss buttons.
+ */
+add_task(async function test_dismiss_tab() {
+ const TAB_UPDATE_TIME_MS = 5;
+ Services.obs.notifyObservers(null, "browser:purge-session-history");
+ Assert.equal(
+ SessionStore.getClosedTabCountForWindow(window),
+ 0,
+ "Closed tab count after purging session history"
+ );
+ await clearAllParentTelemetryEvents();
+
+ await withFirefoxView({}, async browser => {
+ const { document } = browser.contentWindow;
+
+ const closedObjectsChanged = () =>
+ TestUtils.topicObserved("sessionstore-closed-objects-changed");
+
+ const tab1 = await add_new_tab(URLs[0]);
+ const tab2 = await add_new_tab(URLs[1]);
+ const tab3 = await add_new_tab(URLs[2]);
+
+ await close_tab(tab3);
+ await closedObjectsChanged();
+
+ await close_tab(tab2);
+ await closedObjectsChanged();
+
+ await close_tab(tab1);
+ await closedObjectsChanged();
+
+ await clearAllParentTelemetryEvents();
+
+ const tabsList = document.querySelector("ol.closed-tabs-list");
+ const numOfListItems = tabsList.children.length;
+ const lastListItem = tabsList.children[numOfListItems - 1];
+ const timeLabel = lastListItem.querySelector("span.closed-tab-li-time");
+ let initialTimeText = timeLabel.textContent;
+ Assert.stringContains(
+ initialTimeText,
+ "Just now",
+ "recently-closed-tabs list item time is 'Just now'"
+ );
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.tabs.firefox-view.updateTimeMs", TAB_UPDATE_TIME_MS]],
+ });
+
+ await BrowserTestUtils.waitForMutationCondition(
+ timeLabel,
+ { childList: true },
+ () => !timeLabel.textContent.includes("Just now")
+ );
+
+ isnot(
+ timeLabel.textContent,
+ initialTimeText,
+ "recently-closed-tabs list item time has updated"
+ );
+
+ await dismiss_tab(tabsList.children[0], content);
+
+ await TestUtils.waitForCondition(
+ () => {
+ let events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
+ false
+ ).parent;
+ return events && events.length >= 1;
+ },
+ "Waiting for dismiss_closed_tab firefoxview telemetry event.",
+ 200,
+ 100
+ );
+
+ TelemetryTestUtils.assertEvents(
+ RECENTLY_CLOSED_DISMISS_EVENT,
+ { category: "firefoxview" },
+ { clear: true, process: "parent" }
+ );
+
+ Assert.equal(
+ tabsList.children[0].dataset.targeturi,
+ URLs[1],
+ `First recently closed item should be ${URLs[1]}`
+ );
+
+ Assert.equal(
+ tabsList.children.length,
+ 2,
+ "recently-closed-tabs-list should have two list items"
+ );
+
+ await clearAllParentTelemetryEvents();
+
+ await dismiss_tab(tabsList.children[0], content);
+
+ await TestUtils.waitForCondition(
+ () => {
+ let events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
+ false
+ ).parent;
+ return events && events.length >= 1;
+ },
+ "Waiting for dismiss_closed_tab firefoxview telemetry event.",
+ 200,
+ 100
+ );
+
+ TelemetryTestUtils.assertEvents(
+ RECENTLY_CLOSED_DISMISS_EVENT,
+ { category: "firefoxview" },
+ { clear: true, process: "parent" }
+ );
+
+ Assert.equal(
+ tabsList.children[0].dataset.targeturi,
+ URLs[2],
+ `First recently closed item should be ${URLs[2]}`
+ );
+
+ Assert.equal(
+ tabsList.children.length,
+ 1,
+ "recently-closed-tabs-list should have one list item"
+ );
+
+ await clearAllParentTelemetryEvents();
+
+ await dismiss_tab(tabsList.children[0], content);
+
+ await TestUtils.waitForCondition(
+ () => {
+ let events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
+ false
+ ).parent;
+ return events && events.length >= 1;
+ },
+ "Waiting for dismiss_closed_tab firefoxview telemetry event.",
+ 200,
+ 100
+ );
+
+ TelemetryTestUtils.assertEvents(
+ RECENTLY_CLOSED_DISMISS_EVENT,
+ { category: "firefoxview" },
+ { clear: true, process: "parent" }
+ );
+
+ Assert.ok(
+ document.getElementById("recently-closed-tabs-placeholder"),
+ "The empty message is displayed."
+ );
+
+ Assert.ok(
+ !document.querySelector("ol.closed-tabs-list"),
+ "The recently closed tabs list is not displayed."
+ );
+
+ await SpecialPowers.popPrefEnv();
+ });
+});
+
+/**
+ * Asserts that the actionable part of each list item is role="button".
+ * Discussion on why we want a button role can be seen here:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1789875#c1
+ */
+add_task(async function test_button_role() {
+ Services.obs.notifyObservers(null, "browser:purge-session-history");
+ Assert.equal(
+ SessionStore.getClosedTabCountForWindow(window),
+ 0,
+ "Closed tab count after purging session history"
+ );
+
+ await withFirefoxView({}, async browser => {
+ const { document } = browser.contentWindow;
+
+ clearHistory();
+
+ await open_then_close(URLs[0]);
+ await open_then_close(URLs[1]);
+ await open_then_close(URLs[2]);
+
+ await EventUtils.synthesizeMouseAtCenter(
+ gBrowser.ownerDocument.getElementById("firefox-view-button"),
+ { type: "mousedown" },
+ window
+ );
+
+ const tabsList = document.querySelector("ol.closed-tabs-list");
+
+ Array.from(tabsList.children).forEach(tabItem => {
+ let actionableElement = tabItem.querySelector(".closed-tab-li-main");
+ Assert.ok(actionableElement.getAttribute("role"), "button");
+ });
+ });
+});