Bug 1905944: Fix intermittents in browser_tab_preview.js r=sthompson,tabbrowser-reviewers, a=test-only

This patch fixes a number of issues that clear up a few different
intermittents in browser_tab_preview.js.

1. In browser_tab_groups_tab_interactions_telemetry.js, a call to "close
   duplicate tabs" would automatically open the confirmation hint panel.
   If not manually closed, it seems to persist in a half-way open state
   where the `animating` attribute is always `true`. This is now fixed.

2. There is a test somewhere that seems to apply the
   `sidebar.verticalTabs` pref and doesn't turn it off. I would like to
   figure out which test is causing this, but as a last defence I have
   made sure this is always properly set within this test suite.

3. Some tests check that THP is disabled if another panel is open. These
   used to work by opening the `appMenu-popup` menu (the hamburger menu
   in the top right of the browser). The problem with this approach is
   that this panel (and basically every other panel) are very complex
   and are prone to change. This was causing an intermittent failure
   where attempting to close this panel threw an error in console that
   seemed to be specifically related to this panel. I fixed this by
   creating a fake minimal panel that is guaranteed to not change as
   application code changes. (I borrowed this technique from
   https://searchfox.org/mozilla-central/source/browser/base/content/test/popupNotifications/browser_popupNotification_queue.js).

4. There is a suite of tests that checks that THP is disabled in the
   event that a drag operation is performed. The problem is that
   dragging a tab by 100px sometimes detaches the tab and creates a new
   window, breaking the test run. Reducing the drag amount seems to fix
   this.

5. As a last line of defence, I have added a state reset function at the
   start of each test to ensure that all panels are closed and that the
   mouse is not hovering over the tab strip. (The latter was being done at
   the end of some tests already, but this makes sure it is consistently
   applied everywhere.)

Closes bug1898099, bug1905944, bug1933597.

Differential Revision: https://phabricator.services.mozilla.com/D258976
This commit is contained in:
Jeremy Swinarton
2025-07-28 21:03:21 +00:00
committed by rvandermeulen@mozilla.com
parent 2ce3f80962
commit 520c64e4d9
2 changed files with 106 additions and 95 deletions

View File

@@ -295,6 +295,13 @@ add_task(async function test_tabInteractionsCloseViaAnotherTabContext() {
); );
await assertMetricFoundFor("close_tab_other", 4); await assertMetricFoundFor("close_tab_other", 4);
// Dismiss the confirmation panel that appears after closing duplicate tabs.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {});
await BrowserTestUtils.waitForCondition(
() => !window.ConfirmationHint._panel.getAttribute("animating"),
"Ensure the confirmation panel is closed"
);
window.gBrowser.removeAllTabsBut(initialTab); window.gBrowser.removeAllTabsBut(initialTab);
await resetTelemetry(); await resetTelemetry();
}); });

View File

@@ -42,15 +42,53 @@ async function closePreviews(win = window) {
return previewHidden; return previewHidden;
} }
function getOpenPanels() {
return document.querySelectorAll(
"panel[panelopen=true],panel[animating=true],menupopup[open=true]"
);
}
async function resetState() {
for (let panel of getOpenPanels()) {
let hiddenEvent = BrowserTestUtils.waitForPopupEvent(panel, "hidden");
panel.hidePopup();
await hiddenEvent;
}
let openPanels = getOpenPanels();
Assert.ok(!openPanels.length, `sanity check: no panels open`);
// Ensure the mouse is not hovering over the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}
function createFakePanel(win = window) {
let panel = win.document.createXULElement("panel");
// Necessary to get the panel open, animating, etc. elements to appear.
panel.setAttribute("type", "arrow");
win.document.documentElement.appendChild(panel);
return panel;
}
add_setup(async function () { add_setup(async function () {
await SpecialPowers.pushPrefEnv({ await SpecialPowers.pushPrefEnv({
set: [ set: [
["browser.tabs.hoverPreview.enabled", true], ["browser.tabs.hoverPreview.enabled", true],
["browser.tabs.hoverPreview.showThumbnails", false], ["browser.tabs.hoverPreview.showThumbnails", false],
["browser.tabs.tooltipsShowPidAndActiveness", false], ["browser.tabs.tooltipsShowPidAndActiveness", false],
["sidebar.revamp", false],
["sidebar.verticalTabs", false],
["ui.tooltip.delay_ms", 0], ["ui.tooltip.delay_ms", 0],
], ],
}); });
await resetState();
registerCleanupFunction(async function () {
await resetState();
});
}); });
/** /**
@@ -88,10 +126,7 @@ add_task(async function hoverTests() {
BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2); BrowserTestUtils.removeTab(tab2);
// Move the mouse outside of the tab strip. await resetState();
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
// Bug 1897475 - don't show tab previews in background windows // Bug 1897475 - don't show tab previews in background windows
@@ -140,11 +175,8 @@ add_task(async function noTabPreviewInBackgroundWindowTests() {
BrowserTestUtils.removeTab(bgTab); BrowserTestUtils.removeTab(bgTab);
// Move the mouse outside of the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
sinon.restore(); sinon.restore();
await resetState();
}); });
/** /**
@@ -179,10 +211,7 @@ add_task(async function focusTests() {
BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2); BrowserTestUtils.removeTab(tab2);
// Move the mouse outside of the tab strip. await resetState();
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
/** /**
@@ -208,13 +237,8 @@ add_task(async function pidAndActivenessHiddenByDefaultTests() {
); );
await closePreviews(); await closePreviews();
BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab1);
await resetState();
// Move the mouse outside of the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
add_task(async function pidAndActivenessTests() { add_task(async function pidAndActivenessTests() {
@@ -271,11 +295,7 @@ add_task(async function pidAndActivenessTests() {
BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2); BrowserTestUtils.removeTab(tab2);
await SpecialPowers.popPrefEnv(); await SpecialPowers.popPrefEnv();
await resetState();
// Move the mouse outside of the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
/** /**
@@ -334,11 +354,7 @@ add_task(async function thumbnailTests() {
// Removing the tab should close the preview. // Removing the tab should close the preview.
await previewHidden; await previewHidden;
await resetState();
// Move the mouse outside of the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
/** /**
@@ -396,11 +412,7 @@ add_task(async function wireframeTests() {
// Removing the tab should close the preview. // Removing the tab should close the preview.
await previewHidden; await previewHidden;
await resetState();
// Move the mouse outside of the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
/** /**
@@ -447,6 +459,7 @@ add_task(async function delayTests() {
BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2); BrowserTestUtils.removeTab(tab2);
sinon.restore(); sinon.restore();
await resetState();
}); });
/** /**
@@ -474,7 +487,7 @@ add_task(async function dragTests() {
EventUtils.synthesizePlainDragAndDrop({ EventUtils.synthesizePlainDragAndDrop({
srcElement: tab1, srcElement: tab1,
destElement: null, destElement: null,
stepX: 100, stepX: 5,
stepY: 0, stepY: 0,
}); });
@@ -489,12 +502,8 @@ add_task(async function dragTests() {
BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab1);
sinon.restore(); sinon.restore();
// Move the mouse outside of the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
await SpecialPowers.popPrefEnv(); await SpecialPowers.popPrefEnv();
await resetState();
}); });
/** /**
@@ -537,11 +546,7 @@ add_task(async function panelSuppressionOnContextMenuTests() {
contentAreaContextMenu.hidePopup(); contentAreaContextMenu.hidePopup();
BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab);
sinon.restore(); sinon.restore();
await resetState();
// Move the mouse outside of the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
/** /**
@@ -559,11 +564,13 @@ add_task(async function panelSuppressionOnPanelTests() {
const previewComponent = gBrowser.tabContainer.previewPanel; const previewComponent = gBrowser.tabContainer.previewPanel;
sinon.spy(previewComponent, "activate"); sinon.spy(previewComponent, "activate");
// The `openPopup` API appears to not be working for this panel, let fakePanel = createFakePanel();
// but it can be triggered by firing a click event on the associated button. const popupShownEvent = BrowserTestUtils.waitForPopupEvent(
const appMenuButton = document.getElementById("PanelUI-menu-button"); fakePanel,
const appMenuPopup = document.getElementById("appMenu-popup"); "shown"
appMenuButton.click(); );
fakePanel.openPopup();
await popupShownEvent;
EventUtils.synthesizeMouseAtCenter(tab, { type: "mouseover" }, window); EventUtils.synthesizeMouseAtCenter(tab, { type: "mouseover" }, window);
@@ -584,12 +591,12 @@ add_task(async function panelSuppressionOnPanelTests() {
window window
); );
const popupHidingEvent = BrowserTestUtils.waitForEvent( const popupHiddenEvent = BrowserTestUtils.waitForPopupEvent(
appMenuPopup, fakePanel,
"popuphiding" "hidden"
); );
appMenuPopup.hidePopup(); fakePanel.hidePopup();
await popupHidingEvent; await popupHiddenEvent;
// Attempt to open the tab preview immediately after the popup hiding event // Attempt to open the tab preview immediately after the popup hiding event
await openPreview(tab); await openPreview(tab);
@@ -597,11 +604,8 @@ add_task(async function panelSuppressionOnPanelTests() {
BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab);
sinon.restore(); sinon.restore();
fakePanel.remove();
// Move the mouse outside of the tab strip. await resetState();
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
/** /**
@@ -614,11 +618,13 @@ add_task(async function panelSuppressionOnPanelLazyLoadTests() {
let fgWindow = await BrowserTestUtils.openNewBrowserWindow(); let fgWindow = await BrowserTestUtils.openNewBrowserWindow();
let fgTab = fgWindow.gBrowser.tabs[0]; let fgTab = fgWindow.gBrowser.tabs[0];
// The `openPopup` API appears to not be working for this panel, let fakePanel = createFakePanel(fgWindow);
// but it can be triggered by firing a click event on the associated button. const popupShownEvent = BrowserTestUtils.waitForPopupEvent(
const appMenuButton = fgWindow.document.getElementById("PanelUI-menu-button"); fakePanel,
const appMenuPopup = fgWindow.document.getElementById("appMenu-popup"); "shown"
appMenuButton.click(); );
fakePanel.openPopup();
await popupShownEvent;
EventUtils.synthesizeMouseAtCenter(fgTab, { type: "mouseover" }, fgWindow); EventUtils.synthesizeMouseAtCenter(fgTab, { type: "mouseover" }, fgWindow);
@@ -626,8 +632,8 @@ add_task(async function panelSuppressionOnPanelLazyLoadTests() {
// Sometimes the tests run slower than the test browser -- it's not always possible // Sometimes the tests run slower than the test browser -- it's not always possible
// to catch the panel in its opening state, so we have to check for both states. // to catch the panel in its opening state, so we have to check for both states.
return ( return (
(appMenuPopup.getAttribute("animating") === "true" || (fakePanel.getAttribute("animating") === "true" ||
appMenuPopup.getAttribute("panelopen") === "true") && fakePanel.getAttribute("panelopen") === "true") &&
fgWindow.gBrowser.tabContainer.previewPanel !== null fgWindow.gBrowser.tabContainer.previewPanel !== null
); );
}); });
@@ -635,7 +641,7 @@ add_task(async function panelSuppressionOnPanelLazyLoadTests() {
// We can't spy on the previewComponent and check for calls to `activate` like in other tests, // We can't spy on the previewComponent and check for calls to `activate` like in other tests,
// since we can't guarantee that the spy will be set up before the call is made. // since we can't guarantee that the spy will be set up before the call is made.
// Therefore the only realiable way to test that the popup isn't open is to reach in and check // Therefore the only reliable way to test that the popup isn't open is to reach in and check
// that it is in a disabled state. // that it is in a disabled state.
Assert.equal(previewComponent._isDisabled(), true, ""); Assert.equal(previewComponent._isDisabled(), true, "");
@@ -651,20 +657,17 @@ add_task(async function panelSuppressionOnPanelLazyLoadTests() {
fgWindow fgWindow
); );
const popupHidingEvent = BrowserTestUtils.waitForEvent( const popupHiddenEvent = BrowserTestUtils.waitForPopupEvent(
appMenuPopup, fakePanel,
"popuphiding" "hidden"
); );
appMenuPopup.hidePopup(); fakePanel.hidePopup();
await popupHidingEvent; await popupHiddenEvent;
BrowserTestUtils.removeTab(fgTab); BrowserTestUtils.removeTab(fgTab);
fakePanel.remove();
// Move the mouse outside of the tab strip.
await BrowserTestUtils.closeWindow(fgWindow); await BrowserTestUtils.closeWindow(fgWindow);
EventUtils.synthesizeMouseAtCenter(document.documentElement, { await resetState();
type: "mouseover",
});
}); });
/** /**
@@ -712,12 +715,13 @@ add_task(async function otherPanelOpenTests() {
Assert.ok(previewComponent._panelOpener._timer, "Timer is set"); Assert.ok(previewComponent._panelOpener._timer, "Timer is set");
// Open the popup before the timer finishes... let fakePanel = createFakePanel();
const appMenuButton = document.getElementById("PanelUI-menu-button"); const popupShownEvent = BrowserTestUtils.waitForPopupEvent(
const appMenuPopup = document.getElementById("appMenu-popup"); fakePanel,
appMenuButton.click(); "shown"
);
await BrowserTestUtils.waitForEvent(appMenuPopup, "popupshown"); fakePanel.openPopup();
await popupShownEvent;
// Wait for timer to finish... // Wait for timer to finish...
await BrowserTestUtils.waitForCondition( await BrowserTestUtils.waitForCondition(
@@ -738,16 +742,17 @@ add_task(async function otherPanelOpenTests() {
type: "mouseout", type: "mouseout",
}); });
const popupHidingEvent = BrowserTestUtils.waitForEvent( const popupHiddenEvent = BrowserTestUtils.waitForPopupEvent(
appMenuPopup, fakePanel,
"popuphiding" "hidden"
); );
appMenuPopup.hidePopup(); fakePanel.hidePopup();
await popupHidingEvent; await popupHiddenEvent;
fakePanel.remove();
sinon.restore(); sinon.restore();
await SpecialPowers.popPrefEnv(); await SpecialPowers.popPrefEnv();
await resetState();
}); });
/** /**
@@ -782,6 +787,7 @@ add_task(async function urlBarInputTests() {
Assert.equal(previewElement.state, "closed", "Preview is closed"); Assert.equal(previewElement.state, "closed", "Preview is closed");
BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab1);
await resetState();
}); });
/** /**
@@ -812,8 +818,8 @@ add_task(async function zeroDelayTests() {
await closePreviews(); await closePreviews();
BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab);
await SpecialPowers.popPrefEnv(); await SpecialPowers.popPrefEnv();
await resetState();
}); });
/** /**
@@ -852,11 +858,7 @@ add_task(async function wheelTests() {
while (gBrowser.tabs.length > 1) { while (gBrowser.tabs.length > 1) {
BrowserTestUtils.removeTab(gBrowser.tabs[0]); BrowserTestUtils.removeTab(gBrowser.tabs[0]);
} }
await resetState();
// Move the mouse outside of the tab strip.
EventUtils.synthesizeMouseAtCenter(document.documentElement, {
type: "mouseover",
});
}); });
add_task(async function appearsAsTooltipToAccessibilityToolsTests() { add_task(async function appearsAsTooltipToAccessibilityToolsTests() {
@@ -903,6 +905,7 @@ add_task(async function tabContentChangeTests() {
await closePreviews(); await closePreviews();
BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab);
await resetState();
}); });
/** /**
@@ -934,4 +937,5 @@ add_task(async function tabPreview_verticalTabsPositioning() {
await closePreviews(); await closePreviews();
BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab);
await resetState();
}); });