test: Added tests for split view, p=#9667, c=tests

This commit is contained in:
mr. m
2025-07-28 17:05:23 +02:00
committed by GitHub
parent 36a84261b3
commit dfd2830ceb
20 changed files with 599 additions and 108 deletions

View File

@@ -1,5 +1,5 @@
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 8e839c497bba9de04948ad8759679b6a6f61a65f..877ec60553fc64fea860de297dc0858eb05bae7f 100644 index 8e839c497bba9de04948ad8759679b6a6f61a65f..f94a160427b7e465e2c7134fbaf876f589a3fcce 100644
--- a/browser/base/content/browser.js --- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js +++ b/browser/base/content/browser.js
@@ -29,6 +29,7 @@ ChromeUtils.defineESModuleGetters(this, { @@ -29,6 +29,7 @@ ChromeUtils.defineESModuleGetters(this, {
@@ -10,18 +10,16 @@ index 8e839c497bba9de04948ad8759679b6a6f61a65f..877ec60553fc64fea860de297dc0858e
DevToolsSocketStatus: DevToolsSocketStatus:
"resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs", "resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs",
DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs", DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs",
@@ -2282,6 +2283,10 @@ var XULBrowserWindow = { @@ -2282,6 +2283,8 @@ var XULBrowserWindow = {
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser); AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
TranslationsParent.onLocationChange(gBrowser.selectedBrowser); TranslationsParent.onLocationChange(gBrowser.selectedBrowser);
+ gZenViewSplitter.onLocationChange(gBrowser.selectedBrowser);
+ gZenWorkspaces.onLocationChange(gBrowser.selectedBrowser);
+ gZenPinnedTabManager.onLocationChange(gBrowser.selectedBrowser); + gZenPinnedTabManager.onLocationChange(gBrowser.selectedBrowser);
+ +
PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser); PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser);
if (!gMultiProcessBrowser) { if (!gMultiProcessBrowser) {
@@ -4617,7 +4622,7 @@ function switchToTabHavingURI( @@ -4617,7 +4620,7 @@ function switchToTabHavingURI(
ignoreQueryString || replaceQueryString, ignoreQueryString || replaceQueryString,
ignoreFragmentWhenComparing ignoreFragmentWhenComparing
); );
@@ -30,7 +28,7 @@ index 8e839c497bba9de04948ad8759679b6a6f61a65f..877ec60553fc64fea860de297dc0858e
for (let i = 0; i < browsers.length; i++) { for (let i = 0; i < browsers.length; i++) {
let browser = browsers[i]; let browser = browsers[i];
let browserCompare = cleanURL( let browserCompare = cleanURL(
@@ -4660,7 +4665,7 @@ function switchToTabHavingURI( @@ -4660,7 +4663,7 @@ function switchToTabHavingURI(
} }
if (!doAdopt) { if (!doAdopt) {
@@ -39,7 +37,7 @@ index 8e839c497bba9de04948ad8759679b6a6f61a65f..877ec60553fc64fea860de297dc0858e
} }
return true; return true;
@@ -5476,6 +5481,9 @@ var ConfirmationHint = { @@ -5476,6 +5479,9 @@ var ConfirmationHint = {
MozXULElement.insertFTLIfNeeded("toolkit/branding/brandings.ftl"); MozXULElement.insertFTLIfNeeded("toolkit/branding/brandings.ftl");
MozXULElement.insertFTLIfNeeded("browser/confirmationHints.ftl"); MozXULElement.insertFTLIfNeeded("browser/confirmationHints.ftl");
document.l10n.setAttributes(this._message, messageId, options.l10nArgs); document.l10n.setAttributes(this._message, messageId, options.l10nArgs);

View File

@@ -0,0 +1,60 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<commandset id="zenCommandSet">
<command id="cmd_zenCompactModeToggle" />
<command id="cmd_zenCompactModeShowSidebar" />
<command id="cmd_zenCompactModeShowToolbar" />
<command id="cmd_zenCompactModeHideSidebar" />
<command id="cmd_zenCompactModeHideToolbar" />
<command id="cmd_zenCompactModeHideBoth" />
<command id="cmd_zenWorkspaceForward" />
<command id="cmd_zenWorkspaceBackward" />
<command id="cmd_zenSplitViewGrid" />
<command id="cmd_zenSplitViewVertical" />
<command id="cmd_zenSplitViewHorizontal" />
<command id="cmd_zenSplitViewUnsplit" />
<command id="cmd_zenSplitViewLinkInNewTab" />
<command id="cmd_zenSplitViewContextMenu" />
<!-- Workspace commands -->
<command id="cmd_zenWorkspaceSwitch1" />
<command id="cmd_zenWorkspaceSwitch2" />
<command id="cmd_zenWorkspaceSwitch3" />
<command id="cmd_zenWorkspaceSwitch4" />
<command id="cmd_zenWorkspaceSwitch5" />
<command id="cmd_zenWorkspaceSwitch6" />
<command id="cmd_zenWorkspaceSwitch7" />
<command id="cmd_zenWorkspaceSwitch8" />
<command id="cmd_zenWorkspaceSwitch9" />
<command id="cmd_zenWorkspaceSwitch10" />
<command id="cmd_zenOpenZenThemePicker" />
<command id="cmd_zenChangeWorkspaceTab" />
<command id="cmd_zenToggleTabsOnRight" />
<command id="cmd_zenReplacePinnedUrlWithCurrent" />
<command id="cmd_contextZenAddToEssentials" />
<command id="cmd_contextZenRemoveFromEssentials" />
<command id="cmd_zenCtxDeleteWorkspace" />
<command id="cmd_zenChangeWorkspaceName" />
<command id="cmd_zenChangeWorkspaceIcon" />
<command id="cmd_zenReorderWorkspaces" />
<command id="cmd_zenOpenWorkspaceCreation" />
<command id="cmd_zenPinnedTabReset" />
<command id="cmd_zenPinnedTabResetNoTab" />
<command id="cmd_zenToggleSidebar" />
<command id="cmd_zenCopyCurrentURL" />
<command id="cmd_zenCopyCurrentURLMarkdown" />
<command id="cmd_zenGlanceClose" />
<command id="cmd_zenGlanceExpand" />
<command id="cmd_zenGlanceSplit" />
</commandset>

View File

@@ -2,61 +2,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
<commandset id="zenCommandSet"> #include zen-commands.inc.xhtml
<command id="cmd_zenCompactModeToggle" />
<command id="cmd_zenCompactModeShowSidebar" />
<command id="cmd_zenCompactModeShowToolbar" />
<command id="cmd_zenCompactModeHideSidebar" />
<command id="cmd_zenCompactModeHideToolbar" />
<command id="cmd_zenCompactModeHideBoth" />
<command id="cmd_zenWorkspaceForward" />
<command id="cmd_zenWorkspaceBackward" />
<command id="cmd_zenSplitViewGrid" />
<command id="cmd_zenSplitViewVertical" />
<command id="cmd_zenSplitViewHorizontal" />
<command id="cmd_zenSplitViewUnsplit" />
<command id="cmd_zenSplitViewLinkInNewTab" />
<command id="cmd_zenSplitViewContextMenu" />
<!-- Workspace commands -->
<command id="cmd_zenWorkspaceSwitch1" />
<command id="cmd_zenWorkspaceSwitch2" />
<command id="cmd_zenWorkspaceSwitch3" />
<command id="cmd_zenWorkspaceSwitch4" />
<command id="cmd_zenWorkspaceSwitch5" />
<command id="cmd_zenWorkspaceSwitch6" />
<command id="cmd_zenWorkspaceSwitch7" />
<command id="cmd_zenWorkspaceSwitch8" />
<command id="cmd_zenWorkspaceSwitch9" />
<command id="cmd_zenWorkspaceSwitch10" />
<command id="cmd_zenOpenZenThemePicker" />
<command id="cmd_zenChangeWorkspaceTab" />
<command id="cmd_zenToggleTabsOnRight" />
<command id="cmd_zenReplacePinnedUrlWithCurrent" />
<command id="cmd_contextZenAddToEssentials" />
<command id="cmd_contextZenRemoveFromEssentials" />
<command id="cmd_zenCtxDeleteWorkspace" />
<command id="cmd_zenChangeWorkspaceName" />
<command id="cmd_zenChangeWorkspaceIcon" />
<command id="cmd_zenReorderWorkspaces" />
<command id="cmd_zenOpenWorkspaceCreation" />
<command id="cmd_zenPinnedTabReset" />
<command id="cmd_zenPinnedTabResetNoTab" />
<command id="cmd_zenToggleSidebar" />
<command id="cmd_zenCopyCurrentURL" />
<command id="cmd_zenCopyCurrentURLMarkdown" />
<command id="cmd_zenGlanceClose" />
<command id="cmd_zenGlanceExpand" />
<command id="cmd_zenGlanceSplit" />
</commandset>
<keyset id="zenKeyset"></keyset> <keyset id="zenKeyset"></keyset>

View File

@@ -603,7 +603,13 @@ var gZenVerticalTabsManager = {
}, },
animateTab(aTab) { animateTab(aTab) {
if (!gZenUIManager.motion || !aTab || !gZenUIManager._hasLoadedDOM || !aTab.isConnected) { if (
!gZenUIManager.motion ||
!aTab ||
!gZenUIManager._hasLoadedDOM ||
!aTab.isConnected ||
gZenUIManager.testingEnabled
) {
return; return;
} }
// get next visible tab // get next visible tab

View File

@@ -223,6 +223,7 @@
this.browserWrapper.setAttribute('has-finished-animation', true); this.browserWrapper.setAttribute('has-finished-animation', true);
this._animating = false; this._animating = false;
this.animatingOpen = false; this.animatingOpen = false;
this.#currentTab.dispatchEvent(new Event('GlanceOpen', { bubbles: true }));
resolve(this.#currentTab); resolve(this.#currentTab);
}); });
}); });
@@ -358,12 +359,12 @@
this.overlay.removeAttribute('fade-out'); this.overlay.removeAttribute('fade-out');
this.browserWrapper.removeAttribute('animate'); this.browserWrapper.removeAttribute('animate');
this.lastCurrentTab = this.#currentTab; const lastCurrentTab = this.#currentTab;
this.overlay.classList.remove('zen-glance-overlay'); this.overlay.classList.remove('zen-glance-overlay');
gBrowser gBrowser
._getSwitcher() ._getSwitcher()
.setTabStateNoAction(this.lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED); .setTabStateNoAction(lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED);
if (!onTabClose) { if (!onTabClose) {
this.#currentParentTab._visuallySelected = false; this.#currentParentTab._visuallySelected = false;
@@ -381,14 +382,15 @@
this.overlay = null; this.overlay = null;
this.contentWrapper = null; this.contentWrapper = null;
this.lastCurrentTab.removeAttribute('zen-glance-tab'); lastCurrentTab.removeAttribute('zen-glance-tab');
this.lastCurrentTab._closingGlance = true; lastCurrentTab._closingGlance = true;
if (!isDifferent) { if (!isDifferent) {
gBrowser.selectedTab = this.#currentParentTab; gBrowser.selectedTab = this.#currentParentTab;
} }
this._ignoreClose = true; this._ignoreClose = true;
gBrowser.removeTab(this.lastCurrentTab, { animate: true, skipPermitUnload: true }); lastCurrentTab.dispatchEvent(new Event('GlanceClose', { bubbles: true }));
gBrowser.removeTab(lastCurrentTab, { animate: true, skipPermitUnload: true });
gBrowser.tabContainer._invalidateCachedTabs(); gBrowser.tabContainer._invalidateCachedTabs();
this.#currentParentTab.removeAttribute('glance-id'); this.#currentParentTab.removeAttribute('glance-id');
@@ -396,7 +398,6 @@
this.#glances.delete(this.#currentGlanceID); this.#glances.delete(this.#currentGlanceID);
this.#currentGlanceID = setNewID; this.#currentGlanceID = setNewID;
this.lastCurrentTab = null;
this._duringOpening = false; this._duringOpening = false;
this._animating = false; this._animating = false;

View File

@@ -3,8 +3,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
FINAL_TARGET_FILES.actors += [ FINAL_TARGET_FILES.actors += [
"actors/ZenGlanceChild.sys.mjs", "actors/ZenGlanceChild.sys.mjs",
"actors/ZenGlanceParent.sys.mjs", "actors/ZenGlanceParent.sys.mjs",
] ]
TESTING_JS_MODULES += ["tests/GlanceTestUtils.sys.mjs"]

View File

@@ -0,0 +1,30 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export function openGlanceOnTab(window, callback, close = true) {
return new Promise(async (resolve) => {
window.gZenGlanceManager
.openGlance({
url: 'https://example.com',
clientX: 0,
clientY: 0,
width: 0,
height: 0,
})
.then(async (glanceTab) => {
await callback(glanceTab);
if (close) {
window.gZenGlanceManager
.closeGlance({
onTabClose: true,
})
.then(() => {
resolve();
});
} else {
resolve();
}
});
});
}

View File

@@ -177,6 +177,7 @@ class nsZenViewSplitter extends ZenDOMOperatedFeature {
if (previousTab && !previousTab.hasAttribute('zen-empty-tab')) { if (previousTab && !previousTab.hasAttribute('zen-empty-tab')) {
this._lastOpenedTab = previousTab; this._lastOpenedTab = previousTab;
} }
this.onLocationChange(event.target.linkedBrowser);
} }
/** /**
@@ -527,7 +528,6 @@ class nsZenViewSplitter extends ZenDOMOperatedFeature {
this._thumnailCanvas.width = 280 * devicePixelRatio; this._thumnailCanvas.width = 280 * devicePixelRatio;
this._thumnailCanvas.height = 140 * devicePixelRatio; this._thumnailCanvas.height = 140 * devicePixelRatio;
} }
const browsers = this._data[this.currentView].tabs.map((t) => t.linkedBrowser); const browsers = this._data[this.currentView].tabs.map((t) => t.linkedBrowser);
browsers.forEach((b) => { browsers.forEach((b) => {
b.style.pointerEvents = 'none'; b.style.pointerEvents = 'none';
@@ -1108,11 +1108,6 @@ class nsZenViewSplitter extends ZenDOMOperatedFeature {
} }
} }
} }
if (this._sessionRestoring) {
return;
}
this.activateSplitView(splitData);
} }
addTabToSplit(tab, splitNode, prepend = true) { addTabToSplit(tab, splitNode, prepend = true) {
@@ -1181,6 +1176,7 @@ class nsZenViewSplitter extends ZenDOMOperatedFeature {
this.applyGridLayout(splitData.layoutTree); this.applyGridLayout(splitData.layoutTree);
this.setTabsDocShellState(splitData.tabs, true); this.setTabsDocShellState(splitData.tabs, true);
this.toggleWrapperDisplay(true); this.toggleWrapperDisplay(true);
window.dispatchEvent(new CustomEvent('ZenViewSplitter:SplitViewActivated'));
} }
calculateLayoutTree(tabs, gridType) { calculateLayoutTree(tabs, gridType) {

View File

@@ -2,33 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { openGlanceOnTab: internalGlanceHandle } = ChromeUtils.importESModule(
'resource://testing-common/GlanceTestUtils.sys.mjs'
);
function openGlanceOnTab(callback, close = true) { function openGlanceOnTab(callback, close = true) {
return new Promise(async (resolve) => { return internalGlanceHandle(window, callback, close);
setTimeout(() => {
gZenGlanceManager
.openGlance({
url: 'https://example.com',
clientX: 0,
clientY: 0,
width: 0,
height: 0,
})
.then(async (glanceTab) => {
await callback(glanceTab);
if (close) {
setTimeout(() => {
gZenGlanceManager
.closeGlance({
onTabClose: true,
})
.then(() => {
resolve();
});
}, 500); // Give tons of time for the glance to close
} else {
resolve();
}
});
}, 500); // Give tons of time for the glance to open
});
} }

View File

@@ -7,6 +7,7 @@ BROWSER_CHROME_MANIFESTS += [
"container_essentials/browser.toml", "container_essentials/browser.toml",
"glance/browser.toml", "glance/browser.toml",
"pinned/browser.toml", "pinned/browser.toml",
"split_view/browser.toml",
"tabs/browser.toml", "tabs/browser.toml",
"urlbar/browser.toml", "urlbar/browser.toml",
"welcome/browser.toml", "welcome/browser.toml",

View File

@@ -0,0 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
[DEFAULT]
support-files = [
"head.js",
"!/zen/tests/glance/head.js",
]
["browser_basic_split_view.js"]
["browser_split_inset_checks.js"]
["browser_split_groups.js"]
["browser_split_browser_duplication.js"]
["browser_split_view_with_glance.js"]

View File

@@ -0,0 +1,31 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Basic_Split_View() {
await basicSplitNTabs(async (tabs) => {
ok(
gBrowser.tabpanels.hasAttribute('zen-split-view'),
'The split view should not have crashed with two tabs in it'
);
});
ok(
!gBrowser.tabpanels.hasAttribute('zen-split-view'),
'Unsplit view should not have crashed with two tabs in it'
);
});
add_task(async function test_Browser_Elements_Attributes() {
await basicSplitNTabs(async (tabs) => {
Assert.equal(
document.querySelectorAll('.browserSidebarContainer[zen-split="true"]').length,
2,
'There should be two split browser sidebars'
);
});
ok(
!document.querySelector('.browserSidebarContainer[zen-split="true"]'),
'There should be no split browser sidebars in unsplit view'
);
});

View File

@@ -0,0 +1,140 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Basic_Split_View_Duplication() {
const [normal, pinned] = await Promise.all([
addTabTo(gBrowser, getUrlForNthTab(1)),
addTabTo(gBrowser, getUrlForNthTab(2)),
]);
const pinEvent = BrowserTestUtils.waitForEvent(pinned, 'TabPinned');
gBrowser.pinTab(pinned);
await pinEvent;
Assert.ok(
gBrowser.tabs.length === 4, // empty + initial + 2 split tabs
'There should be four tabs after pinning the second tab'
);
await createSplitView([normal, pinned], 'grid');
ok(!pinned.group, 'The pinned tab should not be in a split group after duplication');
ok(
normal.group.hasAttribute('split-view-group'),
'The normal tab should be in a split group after duplication'
);
const group = normal.group;
for (const tab of group.tabs) {
Assert.ok(!tab.pinned, 'All tabs in the split group should not be pinned after duplication');
Assert.ok(
tab.splitView,
'All tabs in the split group should be in a split view after duplication'
);
}
Assert.ok(!group.pinned, 'The split group should not be pinned after duplication');
for (const tab of [pinned, ...group.tabs]) {
await BrowserTestUtils.removeTab(tab);
}
});
add_task(async function test_Split_View_Duplication_Both_Pinned() {
const [tab1, tab2] = await Promise.all([
addTabTo(gBrowser, getUrlForNthTab(1)),
addTabTo(gBrowser, getUrlForNthTab(2)),
]);
const pinEvent1 = BrowserTestUtils.waitForEvent(tab1, 'TabPinned');
const pinEvent2 = BrowserTestUtils.waitForEvent(tab2, 'TabPinned');
gBrowser.pinTab(tab1);
gBrowser.pinTab(tab2);
await Promise.all([pinEvent1, pinEvent2]);
Assert.ok(
gBrowser.tabs.length === 4, // empty + initial + 2 split tabs
'There should be four tabs after pinning both tabs'
);
await createSplitView([tab1, tab2], 'grid');
ok(tab1.group, 'The first pinned tab should be in a split group after duplication');
ok(
tab2.group === tab1.group,
'The second pinned tab should be in the same split group after duplication'
);
Assert.equal(
gBrowser.tabs.length,
4,
'There should not be any duplicate tabs after pinning both tabs'
);
for (const tab of tab1.group.tabs) {
Assert.ok(tab.pinned, 'All tabs in the split group should be pinned after duplication');
Assert.ok(
tab.splitView,
'All tabs in the split group should be in a split view after duplication'
);
}
Assert.ok(tab1.group.pinned, 'The split group should be pinned after duplication of both tabs');
for (const tab of tab1.group.tabs) {
await BrowserTestUtils.removeTab(tab);
}
await BrowserTestUtils.removeTab(tab2);
await BrowserTestUtils.removeTab(tab1);
});
add_task(async function test_Split_View_Duplication_Pinned_Essential() {
const existingTabs = gBrowser.tabs;
const [pinned, essential] = await Promise.all([
addTabTo(gBrowser, getUrlForNthTab(1)),
addTabTo(gBrowser, getUrlForNthTab(2)),
]);
const pinEvent = BrowserTestUtils.waitForEvent(pinned, 'TabPinned');
gBrowser.pinTab(pinned);
await pinEvent;
gZenPinnedTabManager.addToEssentials(essential);
Assert.ok(
gBrowser.tabs.length === 4, // empty + initial + 2 split tabs
'There should be four tabs after pinning the first tab and adding the second to essentials'
);
await createSplitView([pinned, essential], 'grid');
ok(
gBrowser.tabs.length === 4 + 2,
'There should be six tabs after creating a split view with the pinned and essential tabs'
);
ok(
!pinned.group,
'The pinned tab should not be in a split group after duplication with an essential tab'
);
ok(
!essential.group,
'The essential tab should not be in a split group after duplication with a pinned tab'
);
for (const tab of gBrowser.tabs) {
if (existingTabs.includes(tab)) {
continue; // Skip if the tab was already present before the test
}
await BrowserTestUtils.removeTab(tab);
}
});
add_task(async function test_Split_View_Duplication_Essential() {
const existingTabs = gBrowser.tabs;
const essentials = await Promise.all(
[...Array(2)].map((_, i) => addTabTo(gBrowser, getUrlForNthTab(i + 1)))
);
essentials.forEach((tab) => {
gZenPinnedTabManager.addToEssentials(tab);
});
ok(
gBrowser.tabs.length === 4, // empty + initial + 2 essential tabs
'There should be four tabs after adding two essential tabs'
);
await createSplitView(essentials, 'grid');
ok(
gBrowser.tabs.length === 4 + 2,
'There should be six tabs after creating a split view with two essential tabs'
);
for (const tab of essentials) {
ok(!tab.group, 'Each essential tab should not be in a split group after duplication');
ok(!tab.splitView, 'Each essential tab should not be in a split view after duplication');
}
for (const tab of gBrowser.tabs) {
if (existingTabs.includes(tab)) {
continue; // Skip if the tab was already present before the test
}
await BrowserTestUtils.removeTab(tab);
}
});

View File

@@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Basic_Split_Groups() {
await basicSplitNTabs(async (tabs) => {
ok(tabs[0].group.hasAttribute('split-view-group'), 'The first tab should be in a split group');
Assert.equal(tabs[0].group.tabs.length, 2, 'The first split group should contain two tabs');
});
});
add_task(async function test_Basic_Split_Groups_Pinning() {
await basicSplitNTabs(async (tabs) => {
const group = tabs[0].group;
ok(group.hasAttribute('split-view-group'), 'The first tab should be in a split group');
const pinEvent = BrowserTestUtils.waitForEvent(tabs[0], 'TabPinned');
gBrowser.pinTab(tabs[0]);
await pinEvent;
for (const tab of tabs) {
ok(tab.pinned, 'All tabs in the split group should be pinned after pinning the first tab');
ok(
tab.group === group,
'All tabs in the split group should remain in the same group after pinning'
);
}
ok(group.pinned, 'The split group should be pinned after pinning a tab');
const unpinEvent = BrowserTestUtils.waitForEvent(tabs[0], 'TabUnpinned');
gBrowser.unpinTab(tabs[0]);
await unpinEvent;
for (const tab of tabs) {
ok(
!tab.pinned,
'All tabs in the split group should be unpinned after unpinning the first tab'
);
ok(
tab.group === group,
'All tabs in the split group should remain in the same group after unpinning'
);
}
ok(!group.pinned, 'The split group should be unpinned after unpinning a tab');
});
});

View File

@@ -0,0 +1,68 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Basic_Split_View_Inset() {
let viewsToCheck = [];
await basicSplitNTabs(async (tabs) => {
viewsToCheck = document.querySelectorAll('.browserSidebarContainer[zen-split="true"]');
ok(viewsToCheck.length, 'There should be split views present');
Assert.equal(
viewsToCheck[0].style.inset,
'0% 50% 0% 0%',
'The split view should have correct inset style'
);
Assert.equal(
viewsToCheck[1].style.inset,
'0% 0% 0% 50%',
'The second split view should have correct inset style'
);
});
for (const view of viewsToCheck) {
Assert.equal(view.style.inset, '', 'The unsplit view should not have correct inset style');
}
});
add_task(async function test_Horizontal_Split_Inset() {
await basicSplitNTabs(async (tabs) => {
const viewsToCheck = document.querySelectorAll('.browserSidebarContainer[zen-split="true"]');
ok(viewsToCheck.length, 'There should be split views present');
Assert.equal(
viewsToCheck[0].style.inset,
'0% 50% 0% 0%',
'The horizontal split view should have correct inset style'
);
Assert.equal(
viewsToCheck[1].style.inset,
'0% 0% 0% 50%',
'The second horizontal split view should have correct inset style'
);
});
});
add_task(async function test_3_Splits_Grid_Inset() {
await basicSplitNTabs(
async (tabs) => {
const viewsToCheck = document.querySelectorAll('.browserSidebarContainer[zen-split="true"]');
ok(viewsToCheck.length, 'There should be split views present');
Assert.equal(
viewsToCheck[0].style.inset,
'0% 0% 50% 50%',
'The first split view should have correct inset style'
);
Assert.equal(
viewsToCheck[1].style.inset,
'50% 0% 0% 50%',
'The second split view should have correct inset style'
);
Assert.equal(
viewsToCheck[2].style.inset,
'0% 50% 0% 0%',
'The third split view should have correct inset style'
);
},
'grid',
3
);
});

View File

@@ -0,0 +1,104 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
const { openGlanceOnTab } = ChromeUtils.importESModule(
'resource://testing-common/GlanceTestUtils.sys.mjs'
);
add_task(async function test_Basic_Split_View_Glance() {
await basicSplitNTabs(async (tabs) => {
await openGlanceOnTab(window, async (glanceTab) => {
ok(
glanceTab.hasAttribute('zen-glance-tab'),
'The glance tab should have the zen-glance-tab attribute'
);
ok(
gBrowser.tabpanels.hasAttribute('zen-split-view'),
'The split view should not have crashed with two tabs in it'
);
});
});
});
add_task(async function test_Basic_Split_View_Glance_Expand() {
await basicSplitNTabs(async (tabs) => {
await openGlanceOnTab(
window,
async (glanceTab) => {
await gZenGlanceManager.fullyOpenGlance();
ok(
!glanceTab.hasAttribute('zen-glance-tab'),
'The glance tab should not have the zen-glance-tab attribute after expanding'
);
ok(!glanceTab.group, 'The glance tab should not be in a split group after expanding');
for (const tab of tabs) {
ok(
tab.group.hasAttribute('split-view-group'),
'All tabs in the split view should still be in a split group after expanding glance'
);
}
const selectedBrowser = document.querySelectorAll('.browserSidebarContainer.deck-selected');
Assert.equal(
selectedBrowser.length,
1,
'There should be one selected browser sidebar after expanding glance'
);
BrowserTestUtils.removeTab(glanceTab);
},
false
);
});
});
add_task(async function test_Basic_Split_View_Glance_No_More_Split() {
await basicSplitNTabs(
async (tabs) => {
await openGlanceOnTab(window, async (glanceTab) => {
ok(
document.getElementById('cmd_zenGlanceSplit').getAttribute('disabled') === 'true',
'The split command should be disabled when glance is open'
);
});
},
'grid',
4
);
});
add_task(async function test_Basic_Split_View_Glance_Split() {
const tab = await addTabTo(gBrowser, getUrlForNthTab(1));
gBrowser.selectedTab = tab;
await openGlanceOnTab(
window,
async (glanceTab) => {
const waitForSplitPromise = BrowserTestUtils.waitForEvent(
window,
'ZenViewSplitter:SplitViewActivated'
);
document.getElementById('cmd_zenGlanceSplit').doCommand();
await waitForSplitPromise;
ok(
!glanceTab.hasAttribute('zen-glance-tab'),
'The glance tab should not have the zen-glance-tab attribute after splitting'
);
ok(
gBrowser.tabpanels.hasAttribute('zen-split-view'),
'The split view should not have crashed with two tabs in it'
);
ok(
glanceTab.group.hasAttribute('split-view-group'),
'The glance tab should be in a split group after splitting'
);
Assert.equal(
tab.group,
glanceTab.group,
'The original tab should be in the same split group as the glance tab after splitting'
);
BrowserTestUtils.removeTab(glanceTab);
},
false
);
BrowserTestUtils.removeTab(tab);
});

View File

@@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
async function addTabTo(targetBrowser, url = 'http://mochi.test:8888/', params = {}) {
params.skipAnimation = true;
const tab = BrowserTestUtils.addTab(targetBrowser, url, params);
const browser = targetBrowser.getBrowserForTab(tab);
await BrowserTestUtils.browserLoaded(browser);
return tab;
}
function getUrlForNthTab(n) {
return `data:text/plain,tab${n}`;
}
async function createSplitView(tabs, type = 'grid') {
const waitForActivationPromise = BrowserTestUtils.waitForEvent(
window,
'ZenViewSplitter:SplitViewActivated'
);
gZenViewSplitter.splitTabs(tabs, type);
await waitForActivationPromise;
await new Promise((resolve) => {
setTimeout(async () => {
resolve();
}, 100);
});
}
async function basicSplitNTabs(callback, type = 'grid', n = 2) {
Assert.greater(n, 1, 'There should be at least two tabs');
Assert.less(n, 5, 'There should be at most four tabs');
const tabs = await Promise.all(
[...Array(n)].map((_, i) => addTabTo(gBrowser, getUrlForNthTab(i + 1)))
);
await createSplitView(tabs, type);
await callback(tabs);
for (const tab of tabs) {
await BrowserTestUtils.removeTab(tab);
}
}

View File

@@ -1,3 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
const { TabGroupTestUtils } = ChromeUtils.importESModule( const { TabGroupTestUtils } = ChromeUtils.importESModule(
'resource://testing-common/TabGroupTestUtils.sys.mjs' 'resource://testing-common/TabGroupTestUtils.sys.mjs'
); );

View File

@@ -0,0 +1,24 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
add_task(async function test_Change_To_Empty() {
const currentWorkspaceUUID = gZenWorkspaces.activeWorkspace;
await gZenWorkspaces.createAndSaveWorkspace('Test Workspace 2');
const workspaces = await gZenWorkspaces._workspaces();
const secondWorkspace = workspaces.workspaces[1];
await gZenWorkspaces.changeWorkspace(secondWorkspace.uuid);
ok(gBrowser.selectedTab === gZenWorkspaces._emptyTab, 'The empty tab should be selected.');
await gZenWorkspaces.removeWorkspace(gZenWorkspaces.activeWorkspace);
ok(
gBrowser.selectedTab !== gZenWorkspaces._emptyTab,
'The empty tab should not be selected anymore.'
);
const workspacesAfterRemove = await gZenWorkspaces._workspaces();
ok(workspacesAfterRemove.workspaces.length === 1, 'One workspace should exist.');
ok(gBrowser.tabs.length === 2, 'There should be two tabs.');
});

View File

@@ -922,6 +922,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
window.addEventListener('TabPinned', tabUpdateListener); window.addEventListener('TabPinned', tabUpdateListener);
window.addEventListener('TabUnpinned', tabUpdateListener); window.addEventListener('TabUnpinned', tabUpdateListener);
window.addEventListener('aftercustomization', tabUpdateListener); window.addEventListener('aftercustomization', tabUpdateListener);
window.addEventListener('TabSelect', this.onLocationChange.bind(this));
window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this)); window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this));
} }
@@ -1621,7 +1622,9 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
} }
); );
} else { } else {
window.requestAnimationFrame(() => {
workspaceElement.style.paddingTop = essentialsHeight + 'px'; workspaceElement.style.paddingTop = essentialsHeight + 'px';
});
} }
} }
} }
@@ -2448,7 +2451,8 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
tab.setAttribute('zen-workspace-id', activeWorkspace.uuid); tab.setAttribute('zen-workspace-id', activeWorkspace.uuid);
} }
async onLocationChange(browser) { async onLocationChange(event) {
let tab = event.target;
gZenCompactModeManager.sidebar.toggleAttribute( gZenCompactModeManager.sidebar.toggleAttribute(
'zen-has-empty-tab', 'zen-has-empty-tab',
gBrowser.selectedTab.hasAttribute('zen-empty-tab') gBrowser.selectedTab.hasAttribute('zen-empty-tab')
@@ -2457,7 +2461,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
return; return;
} }
let tab = gBrowser.getTabForBrowser(browser);
if (tab.hasAttribute('zen-glance-tab')) { if (tab.hasAttribute('zen-glance-tab')) {
// Extract from parent node so we are not selecting the wrong (current) tab // Extract from parent node so we are not selecting the wrong (current) tab
tab = tab.parentNode.closest('.tabbrowser-tab'); tab = tab.parentNode.closest('.tabbrowser-tab');