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

@@ -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);
}

View File

@@ -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",

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(
'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.');
});