diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mail/base/test/browser/browser_paneSplitter.js | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | comm/mail/base/test/browser/browser_paneSplitter.js | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/comm/mail/base/test/browser/browser_paneSplitter.js b/comm/mail/base/test/browser/browser_paneSplitter.js new file mode 100644 index 0000000000..1646703b96 --- /dev/null +++ b/comm/mail/base/test/browser/browser_paneSplitter.js @@ -0,0 +1,572 @@ +/* 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/. */ + +let tabmail = document.getElementById("tabmail"); +registerCleanupFunction(() => { + tabmail.closeOtherTabs(tabmail.tabInfo[0]); +}); + +// Increase this value to slow the test down if you want to see what it is doing. +let MOUSE_DELAY = 0; + +let win, doc; + +let resizingEvents = 0; +let resizedEvents = 0; +let collapsedEvents = 0; +let expandedEvents = 0; + +// This object keeps the test simple by removing the differences between +// horizontal and vertical, and which pane is controlled by the splitter. +let testRunner = { + outer: null, // The container for the splitter and panes. + splitter: null, // The splitter. + resizedIsBefore: null, // Whether resized is before the splitter. + resized: null, // The pane the splitter controls the size of. + fill: null, // The pane that splitter doesn't control. + dimension: null, // Which dimension the splitter resizes. + + getSize(element) { + return element.getBoundingClientRect()[this.dimension]; + }, + + assertElementSizes(size, msg = "") { + Assert.equal( + this.getSize(this.resized), + size, + `Resized element should take up the expected ${this.dimension}: ${msg}` + ); + Assert.equal( + this.getSize(this.fill), + 500 - size, + `Fill element should take up the rest of the ${this.dimension}: ${msg}` + ); + }, + + assertSplitterSize(size, msg = "") { + Assert.equal( + this.splitter[this.dimension], + size, + `Splitter ${this.dimension} should match expected ${size}: ${msg}` + ); + }, + + get minSizeProperty() { + return this.dimension == "width" ? "minWidth" : "minHeight"; + }, + + get maxSizeProperty() { + return this.dimension == "width" ? "maxWidth" : "maxHeight"; + }, + + get collapseSizeAttribute() { + return this.dimension == "width" ? "collapse-width" : "collapse-height"; + }, + + setCollapseSize(size) { + this.splitter.setAttribute(this.collapseSizeAttribute, size); + }, + + clearCollapseSize() { + this.splitter.removeAttribute(this.collapseSizeAttribute); + }, + + async synthMouse(position, type = "mousemove", otherPosition = 50) { + let x, y; + if (!this.resizedIsBefore) { + position = 500 - position; + } + if (this.dimension == "width") { + [x, y] = [position, otherPosition]; + } else { + [x, y] = [otherPosition, position]; + } + EventUtils.synthesizeMouse( + this.splitter.parentNode, + x, + y, + { type, buttons: 1 }, + win + ); + + if (MOUSE_DELAY) { + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(resolve => setTimeout(resolve, MOUSE_DELAY)); + } + + await new Promise(resolve => requestAnimationFrame(resolve)); + }, +}; + +add_setup(async function () { + let tab = tabmail.openTab("contentTab", { + url: "chrome://mochitests/content/browser/comm/mail/base/test/browser/files/paneSplitter.xhtml", + }); + + await BrowserTestUtils.browserLoaded(tab.browser); + tab.browser.focus(); + + win = tab.browser.contentWindow; + doc = win.document; + + win.addEventListener("splitter-resizing", event => resizingEvents++); + win.addEventListener("splitter-resized", event => resizedEvents++); + win.addEventListener("splitter-collapsed", event => collapsedEvents++); + win.addEventListener("splitter-expanded", event => expandedEvents++); +}); + +add_task(async function testHorizontalBefore() { + let outer = doc.getElementById("horizontal-before"); + let resized = outer.querySelector(".resized"); + let splitter = outer.querySelector(`hr[is="pane-splitter"]`); + let fill = outer.querySelector(".fill"); + + Assert.equal(resized.clientWidth, 200); + Assert.equal(fill.clientWidth, 300); + Assert.equal(win.getComputedStyle(splitter).cursor, "ew-resize"); + + testRunner.outer = outer; + testRunner.splitter = splitter; + testRunner.resizedIsBefore = true; + testRunner.resized = resized; + testRunner.fill = fill; + testRunner.dimension = "width"; + + await subtestDrag(); + await subtestDragSizeBounds(); + await subtestDragAutoCollapse(); + await subtestCollapseExpand(); +}); + +add_task(async function testHorizontalAfter() { + let outer = doc.getElementById("horizontal-after"); + let fill = outer.querySelector(".fill"); + let splitter = outer.querySelector(`hr[is="pane-splitter"]`); + let resized = outer.querySelector(".resized"); + + Assert.equal(fill.clientWidth, 300); + Assert.equal(resized.clientWidth, 200); + Assert.equal(win.getComputedStyle(splitter).cursor, "ew-resize"); + + testRunner.outer = outer; + testRunner.splitter = splitter; + testRunner.resizedIsBefore = false; + testRunner.resized = resized; + testRunner.fill = fill; + testRunner.dimension = "width"; + + await subtestDrag(); + await subtestDragSizeBounds(); + await subtestDragAutoCollapse(); + await subtestCollapseExpand(); +}); + +add_task(async function testVerticalBefore() { + let outer = doc.getElementById("vertical-before"); + let resized = outer.querySelector(".resized"); + let splitter = outer.querySelector(`hr[is="pane-splitter"]`); + let fill = outer.querySelector(".fill"); + + Assert.equal(resized.clientHeight, 200); + Assert.equal(fill.clientHeight, 300); + Assert.equal(win.getComputedStyle(splitter).cursor, "ns-resize"); + + testRunner.outer = outer; + testRunner.splitter = splitter; + testRunner.resizedIsBefore = true; + testRunner.resized = resized; + testRunner.fill = fill; + testRunner.dimension = "height"; + + await subtestDrag(); + await subtestDragSizeBounds(); + await subtestDragAutoCollapse(); + await subtestCollapseExpand(); +}); + +add_task(async function testVerticalAfter() { + let outer = doc.getElementById("vertical-after"); + let fill = outer.querySelector(".fill"); + let splitter = outer.querySelector(`hr[is="pane-splitter"]`); + let resized = outer.querySelector(".resized"); + + testRunner.outer = outer; + testRunner.splitter = splitter; + testRunner.resizedIsBefore = false; + testRunner.resized = resized; + testRunner.fill = fill; + testRunner.dimension = "height"; + + Assert.equal(fill.clientHeight, 300); + Assert.equal(resized.clientHeight, 200); + Assert.equal(win.getComputedStyle(splitter).cursor, "ns-resize"); + + await subtestDrag(); + await subtestDragSizeBounds(); + await subtestDragAutoCollapse(); + await subtestCollapseExpand(); +}); + +async function subtestDrag() { + info("subtestDrag"); + resizingEvents = 0; + resizedEvents = 0; + + let originalPosition = testRunner.getSize(testRunner.resized); + let position = 200; + + await testRunner.synthMouse(position, "mousedown"); + + await testRunner.synthMouse(position, "mousemove", 25); + Assert.equal(resizingEvents, 0, "moving up the splitter does nothing"); + await testRunner.synthMouse(position, "mousemove", 75); + Assert.equal(resizingEvents, 0, "moving down the splitter does nothing"); + + position--; + await testRunner.synthMouse(position); + Assert.equal(resizingEvents, 0, "moving 1px does nothing"); + + position--; + await testRunner.synthMouse(position); + Assert.equal(resizingEvents, 0, "moving 2px does nothing"); + + position--; + await testRunner.synthMouse(position); + Assert.equal(resizingEvents, 1, "a resizing event fired"); + + // Drag in steps to the left-hand/top end. + for (; position >= 0; position -= 50) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes(position); + } + + // Drag beyond the left-hand/top end. + position = -50; + await testRunner.synthMouse(position); + testRunner.assertElementSizes(0); + + // Drag in steps to the right-hand/bottom end. + for (let position = 0; position <= 500; position += 50) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes(position); + } + + // Drag beyond the right-hand/bottom end. + position = 550; + await testRunner.synthMouse(position); + testRunner.assertElementSizes(500); + + // Drop. + position = 400; + Assert.equal(resizingEvents, 1, "no more resizing events fired"); + Assert.equal(resizedEvents, 0, "no resized events fired"); + await testRunner.synthMouse(position); + await testRunner.synthMouse(position, "mouseup"); + testRunner.assertElementSizes(400); + Assert.equal(resizingEvents, 1, "no more resizing events fired"); + Assert.equal(resizedEvents, 1, "a resized event fired"); + + // Pick up again. + await testRunner.synthMouse(position, "mousedown"); + + // Move. + for (; position >= originalPosition; position -= 50) { + await testRunner.synthMouse(position); + } + + // Drop. + Assert.equal(resizingEvents, 2, "a resizing event fired"); + Assert.equal(resizedEvents, 1, "no more resized events fired"); + await testRunner.synthMouse(position, "mouseup"); + testRunner.assertElementSizes(originalPosition); + Assert.equal(resizingEvents, 2, "no more resizing events fired"); + Assert.equal(resizedEvents, 2, "a resized event fired"); +} + +async function subtestDragSizeBounds() { + info("subtestDragSizeBounds"); + + let { splitter, resized, fill, minSizeProperty, maxSizeProperty } = + testRunner; + + // Various min or max sizes to set on the resized and fill elements. + // NOTE: the sum of the max sizes is greater than 500px. + // Moreover, the resized element's min size is below 200px, and the max size + // above it. Similarly, the fill element's min size is below 300px. This + // ensures that the initial sizes of 200px and 300px are within their + // respective min-max bounds. + // NOTE: We do not set a max size on the fill element. The grid layout does + // not handle this. Nor is it an expected usage of the splitter. + for (let [minResized, min] of [ + [null, 0], + ["100.5px", 100.5], + ]) { + for (let [maxResized, expectMax1] of [ + [null, 500], + ["360px", 360], + ]) { + for (let [minFill, expectMax2] of [ + [null, 500], + ["148px", 352], + ]) { + info(`Bounds [${minResized}, ${maxResized}] and [${minFill}, none]`); + let max = Math.min(expectMax1, expectMax2); + info(`Overall bound [${min}px, ${max}px]`); + + // Construct a set of positions we are interested in. + let roundMin = Math.floor(min); + let roundMax = Math.ceil(max); + let positionSet = [-50, 150, 350, 550]; + // Include specific positions around the minimum and maximum points. + positionSet.push(roundMin - 1, roundMin, roundMin + 1); + positionSet.push(roundMax - 1, roundMax, roundMax + 1); + positionSet.sort(); + + // Reset the splitter. + splitter.width = null; + splitter.height = null; + + resized.style[minSizeProperty] = minResized; + resized.style[maxSizeProperty] = maxResized; + fill.style[minSizeProperty] = minFill; + + testRunner.assertElementSizes(200, "initial position"); + await testRunner.synthMouse(200, "mousedown"); + + for (let position of positionSet) { + await testRunner.synthMouse(position); + let size = Math.min(Math.max(position, min), max); + testRunner.assertElementSizes(size, `Moved forward to ${position}`); + testRunner.assertSplitterSize(size, `Moved forward to ${position}`); + } + + await testRunner.synthMouse(500); + await testRunner.synthMouse(500, "mouseup"); + testRunner.assertElementSizes(max, "positioned at max"); + testRunner.assertSplitterSize(max, "positioned at max"); + + // Reverse. + await testRunner.synthMouse(max, "mousedown"); + + for (let position of positionSet.reverse()) { + await testRunner.synthMouse(position); + let size = Math.min(Math.max(position, min), max); + testRunner.assertElementSizes(size, `Moved backward to ${position}`); + testRunner.assertSplitterSize(size, `Moved backward to ${position}`); + } + + await testRunner.synthMouse(0); + await testRunner.synthMouse(0, "mouseup"); + testRunner.assertElementSizes(min, "positioned at min"); + testRunner.assertSplitterSize(min, "positioned at min"); + } + } + } + + // Reset. + splitter.width = null; + splitter.height = null; + resized.style[minSizeProperty] = null; + resized.style[maxSizeProperty] = null; + fill.style[minSizeProperty] = null; +} + +async function subtestDragAutoCollapse() { + info("subtestDragAutoCollapse"); + testRunner.setCollapseSize(78); + + collapsedEvents = 0; + expandedEvents = 0; + + let { splitter } = testRunner; + + let originalPosition = 200; + + // Drag in steps toward the left-hand/top end. + await testRunner.synthMouse(200, "mousedown"); + for (let position of [180, 160, 140, 120, 100, 80, 78]) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes( + position, + `Should have ${position} size at ${position}` + ); + Assert.ok(!splitter.isCollapsed, `Should not be collapsed at ${position}`); + } + + // For the first 20 pixels inside the minimum size, nothing happens. + for (let position of [74, 68, 64, 60, 58]) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes( + 78, + `Should be at collapse-size at ${position}` + ); + Assert.ok(!splitter.isCollapsed, `Should not be collapsed at ${position}`); + } + + // Then the pane collapses. + await testRunner.synthMouse(57); + Assert.equal(collapsedEvents, 1, "collapsed event fired"); + for (let position of [57, 55, 51, 40, 20, 0, -20]) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes(0, `Should have no size at ${position}`); + Assert.ok(splitter.isCollapsed, `Should be collapsed at ${position}`); + } + + await testRunner.synthMouse(-20, "mouseup"); + testRunner.assertElementSizes( + 0, + "Should be at min size after releasing mouse" + ); + Assert.ok(splitter.isCollapsed, "Should be collapsed after releasing mouse"); + + // Drag it from the collapsed state. + await testRunner.synthMouse(0, "mousedown"); + for (let position of [0, 8, 16, 19]) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes( + 0, + `Should still have no size at ${position}` + ); + Assert.ok(splitter.isCollapsed, `Should still be collapsed at ${position}`); + } + + // Then the pane expands. For the first 20 pixels, nothing happens. + await testRunner.synthMouse(20); + Assert.equal(expandedEvents, 1, "expanded event fired"); + for (let position of [40, 60, 78]) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes( + 78, + `Should expand to collapse-size at ${position}` + ); + Assert.ok( + !splitter.isCollapsed, + `Should no longer be collapsed at ${position}` + ); + } + + for (let position of [79, 100, 120, 200, 250, 300, 400, 450]) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes( + position, + `Should have ${position} size at ${position}` + ); + Assert.ok(!splitter.isCollapsed, `Should not be collapsed at ${position}`); + } + + await testRunner.synthMouse(450, "mouseup"); + testRunner.assertElementSizes( + 450, + "Should be at final size after releasing mouse" + ); + Assert.ok( + !splitter.isCollapsed, + "Should not be collapsed after releasing mouse" + ); + + // Test that collapse and expand can happen in the same drag. + await testRunner.synthMouse(450, "mousedown"); + let position; + let expectedSize; + for ([position, expectedSize] of [ + [58, 78], + [57, 0], + [58, 78], + [57, 0], + ]) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes( + expectedSize, + `Should have ${expectedSize} size at ${position}` + ); + } + Assert.equal(collapsedEvents, 3, "collapsed events fired"); + Assert.equal(expandedEvents, 2, "expanded events fired"); + await testRunner.synthMouse(position, "mouseup"); + + // Test that expansion from collapsed reverts to normal behaviour after + // dragging out to the minimum size. + await testRunner.synthMouse(0, "mousedown"); + for ([position, expectedSize] of [ + [0, 0], + [10, 0], + [20, 78], + [40, 78], + [60, 78], + [40, 0], + [60, 78], + [40, 0], + [80, 80], + [100, 100], + ]) { + await testRunner.synthMouse(position); + testRunner.assertElementSizes( + expectedSize, + `Should have ${expectedSize} size at ${position}` + ); + } + Assert.equal(collapsedEvents, 5, "collapsed events fired"); + Assert.equal(expandedEvents, 5, "expanded events fired"); + await testRunner.synthMouse(position, "mouseup"); + + // Restore the original position. + await testRunner.synthMouse(position, "mousedown"); + position = originalPosition; + await testRunner.synthMouse(position); + await testRunner.synthMouse(position, "mouseup"); + testRunner.assertElementSizes(originalPosition); + + testRunner.clearCollapseSize(); +} + +async function subtestCollapseExpand() { + info("subtestCollapseExpand"); + collapsedEvents = 0; + expandedEvents = 0; + + let { splitter } = testRunner; + + let originalSize = testRunner.getSize(testRunner.resized); + + // Collapse. + Assert.ok(!splitter.isCollapsed, "splitter is not collapsed"); + Assert.equal(collapsedEvents, 0, "no collapsed events have fired"); + + splitter.collapse(); + testRunner.assertElementSizes(0); + Assert.ok(splitter.isCollapsed, "splitter is collapsed"); + Assert.equal(collapsedEvents, 1, "a collapsed event fired"); + + splitter.collapse(); + Assert.ok(splitter.isCollapsed, "splitter is collapsed"); + Assert.equal(collapsedEvents, 1, "no more collapsed events have fired"); + + // Expand. + splitter.expand(); + testRunner.assertElementSizes(originalSize); + Assert.ok(!splitter.isCollapsed, "splitter is not collapsed"); + Assert.equal(expandedEvents, 1, "an expanded event fired"); + + splitter.expand(); + Assert.ok(!splitter.isCollapsed, "splitter is not collapsed"); + Assert.equal(expandedEvents, 1, "no more expanded events have fired"); + + collapsedEvents = 0; + expandedEvents = 0; + + // Collapse again. Then drag to expand. + splitter.collapse(); + Assert.equal(collapsedEvents, 1, "a collapsed event fired"); + + testRunner.setCollapseSize(78); + + await testRunner.synthMouse(0, "mousedown"); + await testRunner.synthMouse(200); + await testRunner.synthMouse(200, "mouseup"); + testRunner.assertElementSizes(200); + Assert.ok(!splitter.isCollapsed, "splitter is not collapsed"); + Assert.equal(expandedEvents, 1, "an expanded event fired"); + + testRunner.clearCollapseSize(); +} |