From dfd2830ceb5ef68ebcc290dce76c66c150668266 Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:05:23 +0200 Subject: [PATCH] test: Added tests for split view, p=#9667, c=tests --- src/browser/base/content/browser-js.patch | 12 +- .../base/content/zen-commands.inc.xhtml | 60 ++++++++ .../base/content/zen-keysets.inc.xhtml | 57 +------ src/zen/common/ZenUIManager.mjs | 8 +- src/zen/glance/ZenGlanceManager.mjs | 13 +- src/zen/glance/moz.build | 3 +- src/zen/glance/tests/GlanceTestUtils.sys.mjs | 30 ++++ src/zen/split-view/ZenViewSplitter.mjs | 8 +- src/zen/tests/glance/head.js | 33 +---- src/zen/tests/moz.build | 1 + src/zen/tests/split_view/browser.toml | 15 ++ .../split_view/browser_basic_split_view.js | 31 ++++ .../browser_split_browser_duplication.js | 140 ++++++++++++++++++ .../tests/split_view/browser_split_groups.js | 43 ++++++ .../split_view/browser_split_inset_checks.js | 68 +++++++++ .../browser_split_view_with_glance.js | 104 +++++++++++++ src/zen/tests/split_view/head.js | 43 ++++++ src/zen/tests/tabs/head.js | 5 + .../workspaces/browser_change_to_empty.js | 24 +++ src/zen/workspaces/ZenWorkspaces.mjs | 9 +- 20 files changed, 599 insertions(+), 108 deletions(-) create mode 100644 src/browser/base/content/zen-commands.inc.xhtml create mode 100644 src/zen/glance/tests/GlanceTestUtils.sys.mjs create mode 100644 src/zen/tests/split_view/browser.toml create mode 100644 src/zen/tests/split_view/browser_basic_split_view.js create mode 100644 src/zen/tests/split_view/browser_split_browser_duplication.js create mode 100644 src/zen/tests/split_view/browser_split_groups.js create mode 100644 src/zen/tests/split_view/browser_split_inset_checks.js create mode 100644 src/zen/tests/split_view/browser_split_view_with_glance.js create mode 100644 src/zen/tests/split_view/head.js create mode 100644 src/zen/tests/workspaces/browser_change_to_empty.js diff --git a/src/browser/base/content/browser-js.patch b/src/browser/base/content/browser-js.patch index d8ed9df04..d64624dd9 100644 --- a/src/browser/base/content/browser-js.patch +++ b/src/browser/base/content/browser-js.patch @@ -1,5 +1,5 @@ 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 +++ b/browser/base/content/browser.js @@ -29,6 +29,7 @@ ChromeUtils.defineESModuleGetters(this, { @@ -10,18 +10,16 @@ index 8e839c497bba9de04948ad8759679b6a6f61a65f..877ec60553fc64fea860de297dc0858e DevToolsSocketStatus: "resource://devtools/shared/security/DevToolsSocketStatus.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); TranslationsParent.onLocationChange(gBrowser.selectedBrowser); -+ gZenViewSplitter.onLocationChange(gBrowser.selectedBrowser); -+ gZenWorkspaces.onLocationChange(gBrowser.selectedBrowser); + gZenPinnedTabManager.onLocationChange(gBrowser.selectedBrowser); + PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser); if (!gMultiProcessBrowser) { -@@ -4617,7 +4622,7 @@ function switchToTabHavingURI( +@@ -4617,7 +4620,7 @@ function switchToTabHavingURI( ignoreQueryString || replaceQueryString, ignoreFragmentWhenComparing ); @@ -30,7 +28,7 @@ index 8e839c497bba9de04948ad8759679b6a6f61a65f..877ec60553fc64fea860de297dc0858e for (let i = 0; i < browsers.length; i++) { let browser = browsers[i]; let browserCompare = cleanURL( -@@ -4660,7 +4665,7 @@ function switchToTabHavingURI( +@@ -4660,7 +4663,7 @@ function switchToTabHavingURI( } if (!doAdopt) { @@ -39,7 +37,7 @@ index 8e839c497bba9de04948ad8759679b6a6f61a65f..877ec60553fc64fea860de297dc0858e } return true; -@@ -5476,6 +5481,9 @@ var ConfirmationHint = { +@@ -5476,6 +5479,9 @@ var ConfirmationHint = { MozXULElement.insertFTLIfNeeded("toolkit/branding/brandings.ftl"); MozXULElement.insertFTLIfNeeded("browser/confirmationHints.ftl"); document.l10n.setAttributes(this._message, messageId, options.l10nArgs); diff --git a/src/browser/base/content/zen-commands.inc.xhtml b/src/browser/base/content/zen-commands.inc.xhtml new file mode 100644 index 000000000..0bd5f6020 --- /dev/null +++ b/src/browser/base/content/zen-commands.inc.xhtml @@ -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/. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/base/content/zen-keysets.inc.xhtml b/src/browser/base/content/zen-keysets.inc.xhtml index f34bb1191..2e8122fb5 100644 --- a/src/browser/base/content/zen-keysets.inc.xhtml +++ b/src/browser/base/content/zen-keysets.inc.xhtml @@ -2,61 +2,6 @@ # 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/. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#include zen-commands.inc.xhtml diff --git a/src/zen/common/ZenUIManager.mjs b/src/zen/common/ZenUIManager.mjs index 8173287d4..6650435cf 100644 --- a/src/zen/common/ZenUIManager.mjs +++ b/src/zen/common/ZenUIManager.mjs @@ -603,7 +603,13 @@ var gZenVerticalTabsManager = { }, animateTab(aTab) { - if (!gZenUIManager.motion || !aTab || !gZenUIManager._hasLoadedDOM || !aTab.isConnected) { + if ( + !gZenUIManager.motion || + !aTab || + !gZenUIManager._hasLoadedDOM || + !aTab.isConnected || + gZenUIManager.testingEnabled + ) { return; } // get next visible tab diff --git a/src/zen/glance/ZenGlanceManager.mjs b/src/zen/glance/ZenGlanceManager.mjs index 5f54a6058..3e4bc6aee 100644 --- a/src/zen/glance/ZenGlanceManager.mjs +++ b/src/zen/glance/ZenGlanceManager.mjs @@ -223,6 +223,7 @@ this.browserWrapper.setAttribute('has-finished-animation', true); this._animating = false; this.animatingOpen = false; + this.#currentTab.dispatchEvent(new Event('GlanceOpen', { bubbles: true })); resolve(this.#currentTab); }); }); @@ -358,12 +359,12 @@ this.overlay.removeAttribute('fade-out'); this.browserWrapper.removeAttribute('animate'); - this.lastCurrentTab = this.#currentTab; + const lastCurrentTab = this.#currentTab; this.overlay.classList.remove('zen-glance-overlay'); gBrowser ._getSwitcher() - .setTabStateNoAction(this.lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED); + .setTabStateNoAction(lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED); if (!onTabClose) { this.#currentParentTab._visuallySelected = false; @@ -381,14 +382,15 @@ this.overlay = null; this.contentWrapper = null; - this.lastCurrentTab.removeAttribute('zen-glance-tab'); - this.lastCurrentTab._closingGlance = true; + lastCurrentTab.removeAttribute('zen-glance-tab'); + lastCurrentTab._closingGlance = true; if (!isDifferent) { gBrowser.selectedTab = this.#currentParentTab; } 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(); this.#currentParentTab.removeAttribute('glance-id'); @@ -396,7 +398,6 @@ this.#glances.delete(this.#currentGlanceID); this.#currentGlanceID = setNewID; - this.lastCurrentTab = null; this._duringOpening = false; this._animating = false; diff --git a/src/zen/glance/moz.build b/src/zen/glance/moz.build index 3f787914b..9588459dc 100644 --- a/src/zen/glance/moz.build +++ b/src/zen/glance/moz.build @@ -3,8 +3,9 @@ # 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/. - FINAL_TARGET_FILES.actors += [ "actors/ZenGlanceChild.sys.mjs", "actors/ZenGlanceParent.sys.mjs", ] + +TESTING_JS_MODULES += ["tests/GlanceTestUtils.sys.mjs"] diff --git a/src/zen/glance/tests/GlanceTestUtils.sys.mjs b/src/zen/glance/tests/GlanceTestUtils.sys.mjs new file mode 100644 index 000000000..08fbe8dee --- /dev/null +++ b/src/zen/glance/tests/GlanceTestUtils.sys.mjs @@ -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(); + } + }); + }); +} diff --git a/src/zen/split-view/ZenViewSplitter.mjs b/src/zen/split-view/ZenViewSplitter.mjs index cd7c61207..b0271c823 100644 --- a/src/zen/split-view/ZenViewSplitter.mjs +++ b/src/zen/split-view/ZenViewSplitter.mjs @@ -177,6 +177,7 @@ class nsZenViewSplitter extends ZenDOMOperatedFeature { if (previousTab && !previousTab.hasAttribute('zen-empty-tab')) { this._lastOpenedTab = previousTab; } + this.onLocationChange(event.target.linkedBrowser); } /** @@ -527,7 +528,6 @@ class nsZenViewSplitter extends ZenDOMOperatedFeature { this._thumnailCanvas.width = 280 * devicePixelRatio; this._thumnailCanvas.height = 140 * devicePixelRatio; } - const browsers = this._data[this.currentView].tabs.map((t) => t.linkedBrowser); browsers.forEach((b) => { b.style.pointerEvents = 'none'; @@ -1108,11 +1108,6 @@ class nsZenViewSplitter extends ZenDOMOperatedFeature { } } } - - if (this._sessionRestoring) { - return; - } - this.activateSplitView(splitData); } addTabToSplit(tab, splitNode, prepend = true) { @@ -1181,6 +1176,7 @@ class nsZenViewSplitter extends ZenDOMOperatedFeature { this.applyGridLayout(splitData.layoutTree); this.setTabsDocShellState(splitData.tabs, true); this.toggleWrapperDisplay(true); + window.dispatchEvent(new CustomEvent('ZenViewSplitter:SplitViewActivated')); } calculateLayoutTree(tabs, gridType) { diff --git a/src/zen/tests/glance/head.js b/src/zen/tests/glance/head.js index 963fad248..2d884456e 100644 --- a/src/zen/tests/glance/head.js +++ b/src/zen/tests/glance/head.js @@ -2,33 +2,10 @@ * 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/. */ +const { openGlanceOnTab: internalGlanceHandle } = ChromeUtils.importESModule( + 'resource://testing-common/GlanceTestUtils.sys.mjs' +); + function openGlanceOnTab(callback, close = true) { - return new Promise(async (resolve) => { - 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 - }); + return internalGlanceHandle(window, callback, close); } diff --git a/src/zen/tests/moz.build b/src/zen/tests/moz.build index ae9b9a17e..881368a49 100644 --- a/src/zen/tests/moz.build +++ b/src/zen/tests/moz.build @@ -7,6 +7,7 @@ BROWSER_CHROME_MANIFESTS += [ "container_essentials/browser.toml", "glance/browser.toml", "pinned/browser.toml", + "split_view/browser.toml", "tabs/browser.toml", "urlbar/browser.toml", "welcome/browser.toml", diff --git a/src/zen/tests/split_view/browser.toml b/src/zen/tests/split_view/browser.toml new file mode 100644 index 000000000..94b4a923c --- /dev/null +++ b/src/zen/tests/split_view/browser.toml @@ -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"] diff --git a/src/zen/tests/split_view/browser_basic_split_view.js b/src/zen/tests/split_view/browser_basic_split_view.js new file mode 100644 index 000000000..2beb8a291 --- /dev/null +++ b/src/zen/tests/split_view/browser_basic_split_view.js @@ -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' + ); +}); diff --git a/src/zen/tests/split_view/browser_split_browser_duplication.js b/src/zen/tests/split_view/browser_split_browser_duplication.js new file mode 100644 index 000000000..968e58a8a --- /dev/null +++ b/src/zen/tests/split_view/browser_split_browser_duplication.js @@ -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); + } +}); diff --git a/src/zen/tests/split_view/browser_split_groups.js b/src/zen/tests/split_view/browser_split_groups.js new file mode 100644 index 000000000..ab5ab8b9b --- /dev/null +++ b/src/zen/tests/split_view/browser_split_groups.js @@ -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'); + }); +}); diff --git a/src/zen/tests/split_view/browser_split_inset_checks.js b/src/zen/tests/split_view/browser_split_inset_checks.js new file mode 100644 index 000000000..0ceb0950d --- /dev/null +++ b/src/zen/tests/split_view/browser_split_inset_checks.js @@ -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 + ); +}); diff --git a/src/zen/tests/split_view/browser_split_view_with_glance.js b/src/zen/tests/split_view/browser_split_view_with_glance.js new file mode 100644 index 000000000..20a592221 --- /dev/null +++ b/src/zen/tests/split_view/browser_split_view_with_glance.js @@ -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); +}); diff --git a/src/zen/tests/split_view/head.js b/src/zen/tests/split_view/head.js new file mode 100644 index 000000000..5748459e7 --- /dev/null +++ b/src/zen/tests/split_view/head.js @@ -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); + } +} diff --git a/src/zen/tests/tabs/head.js b/src/zen/tests/tabs/head.js index 0784bcafa..ba758a0fc 100644 --- a/src/zen/tests/tabs/head.js +++ b/src/zen/tests/tabs/head.js @@ -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( 'resource://testing-common/TabGroupTestUtils.sys.mjs' ); diff --git a/src/zen/tests/workspaces/browser_change_to_empty.js b/src/zen/tests/workspaces/browser_change_to_empty.js new file mode 100644 index 000000000..c7f6f90b7 --- /dev/null +++ b/src/zen/tests/workspaces/browser_change_to_empty.js @@ -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.'); +}); diff --git a/src/zen/workspaces/ZenWorkspaces.mjs b/src/zen/workspaces/ZenWorkspaces.mjs index 6fe257018..714450ce3 100644 --- a/src/zen/workspaces/ZenWorkspaces.mjs +++ b/src/zen/workspaces/ZenWorkspaces.mjs @@ -922,6 +922,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { window.addEventListener('TabPinned', tabUpdateListener); window.addEventListener('TabUnpinned', tabUpdateListener); window.addEventListener('aftercustomization', tabUpdateListener); + window.addEventListener('TabSelect', this.onLocationChange.bind(this)); window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this)); } @@ -1621,7 +1622,9 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { } ); } else { - workspaceElement.style.paddingTop = essentialsHeight + 'px'; + window.requestAnimationFrame(() => { + workspaceElement.style.paddingTop = essentialsHeight + 'px'; + }); } } } @@ -2448,7 +2451,8 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { tab.setAttribute('zen-workspace-id', activeWorkspace.uuid); } - async onLocationChange(browser) { + async onLocationChange(event) { + let tab = event.target; gZenCompactModeManager.sidebar.toggleAttribute( 'zen-has-empty-tab', gBrowser.selectedTab.hasAttribute('zen-empty-tab') @@ -2457,7 +2461,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature { return; } - let tab = gBrowser.getTabForBrowser(browser); if (tab.hasAttribute('zen-glance-tab')) { // Extract from parent node so we are not selecting the wrong (current) tab tab = tab.parentNode.closest('.tabbrowser-tab');