diff options
| author | erstarr <253168930+erstarr@users.noreply.github.com> | 2026-05-08 17:45:43 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-08 16:45:43 +0100 |
| commit | bd7f695363898826d5bd04fe830f19581ac4c5b2 (patch) | |
| tree | a4dd157b8d851330035b4b6184995fb4ebd9d277 /hyprtester/src/tests/main/scroll.cpp | |
| parent | ac75bcab713490eb000e4fa48f7e7316c3535854 (diff) | |
algo/scrolling: various scrolling view related bugfixes (#13974)
* fix scrolling view move when a grouped window is closed - prevent redundant space assignment for a grouped window that's about to be unmapped, add a `FOCUS_REASON` for unmapping grouped windows
* fix focus_fallback after unmapping a window floating moving view
* fix view change when changing workspaces (special and normal).
add an optional `RECALCULATE_REASON` parameter to `recalculate()` calls of all algorithms.
add an optional `RECALCULATE_MONITOR_REASON` parameter to `recalculateMonitor()` calls.
add 2 new `FOCUS_REASON`s, for workspace change and special workspace toggle
* Fix hyprctl keyword causing scrolling view move.
rename INPUT_MODE_KB to INPUT_MODE_HARD since it seems to have little to do with keybinds
* fix fullscreen/maximise moving the scrolling view
* fix opening of layer 3 programs moving scrolling view
* refactor, clean up and clang-format
* fix bad decisions
* fix includes, clang-format.
* remove TODO
* `focuswindow` no longer moves scrolling view when `follow_focus = 0`
* fix: changes in a group's current window no longer casues scrolling view shift when `follow_focus = 0`
* fix: `moveintogroup`, `movewindoworgroup` no longer causes scrolling view change if `follow_focus = 0`
* clang-format
* add `RECALCULATE_REASON_UNKNOWN`, `RECALCULATE_MONITOR_REASON_UNKNOWN`, `REMOVE_FROM_GROUP_REASON_UNKNOWN`
remove optional wrapper from their enums, refactor code to work with the change
* dispatches that call `switchtoWindow()` in CKeybindManager no longer cause scrolling view to move if `follow_focus = 0`.
They still cause the move if `follow_focus = 1`
Dispatches effected by this: `movefocus`, `cyclenext`, `focuscurrentorlast`, `focuscurrentorurgent`
* Fix for Lua: switchToWondow
* Fix for Lua: focus({window})
* Fix for Lua: Move Window Into Group
* clang-format
* Drop `hyprctl keyword` change
* test: `focus({window})` with `follow_focus = false`
* test: `focus({window})` with `follow_focus = true`
* test: test Focus Fallback
* test: Focus Fallback with groups
* test: workspace change
* test: special workspace toggle
* test: workspace change - special to special
also minor refactoring
* test: close window in a group
* test: move window into group `follow_focus = false`
minor fix in test: expect -> assert
* test: move window into group `follow_focus = true`
* test: new layer
* test: maximise
fix in test: expect to assert.
* test: fullscreen
* test: `dispatch window.focus({ direction })` when `follow_focus = false`
* test: `dispatch window.focus({ direction })` when `follow_focus = true`
test-fix: expect -> assert
* test: move focus within group when `follow_focus = false`
* test: move focus within group when `follow_focus = true`
* test-fix: kill all layers before killing all windows in layers test. prevents internal post test cleanup function failing to kill all layers
test-fix: maximized and fullscreen swapped between their tests
test-fix: testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue missing layout initialization
test-fix: testScrollingViewBehaviourWorkspaceChange, testScrollingViewBehaviourCloseWindowInGroup have stale window class in a comment
clang-format
* catch up with main: move to new test function
* clean-up the clean-up part of the tests: redundant
* clang-format
* pain
* suffering: fix rebase fail
* test: manually kill all layers since the cleanup function fails
* remove test: fullscreen and maximise. scrolling has its own behaviour now
Diffstat (limited to 'hyprtester/src/tests/main/scroll.cpp')
| -rw-r--r-- | hyprtester/src/tests/main/scroll.cpp | 740 |
1 files changed, 739 insertions, 1 deletions
diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 103ea0c8..c3e73994 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -196,7 +196,7 @@ TEST_CASE(scrollWindowRule) { // not the greatest test, but as long as res and gaps don't change, we good. // if this test breaks, it's likely you broke equal sizing - EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 179,1036"); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "size: 179,1036"); } TEST_CASE(scrollFullscreen) { @@ -240,3 +240,741 @@ TEST_CASE(scrollFullscreen) { ASSERT_CONTAINS(str, "class: kitty_scroll_B"); } } + +TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { + + /* + focuswindow DOES NOT move the scrolling view when follow_focus = false + --------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD NOT move scrolling view when follow_focus = false", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("Could not spawn kitty with win class `a`"); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("Could not spawn kitty with win class `b`"); + } + + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // if the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport + const std::string posA = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const int posAx = std::stoi(posA.substr(0, posA.find(','))); + if (posAx < 0) { + NLog::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::GREEN, Colors::RESET, posAx); + } else { + FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, posAx); + } +} + +TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { + + /* + focuswindow DOES move the view when follow_focus = true + -------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD move scrolling view when follow_focus = true", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("Could not spawn kitty with win class `a`"); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("Could not spawn kitty with win class `b`"); + } + + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // If the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport. + // If it is not, the view moved, which is what we expect to happen. + const std::string posA = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const int posAx = std::stoi(posA.substr(0, posA.find(','))); + if (posAx < 0) { + FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be >= 0, got {}.", Colors::RED, Colors::RESET, posAx); + } else { + NLog::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be >= 0, got {}.", Colors::GREEN, Colors::RESET, posAx); + } +} + +TEST_CASE(testScrollingViewBehaviourFocusFallback) { + + /* + Focus fallback from killing a floating window onto a tiled window must NOT move scrolling view, regardless of follow_focus + -------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a tiled window should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with class `c`", Colors::RED); + } + + // make it (window of class:c) float - the view now mush have shifted to fit window class:b + OK(getFromSocket("/dispatch hl.dsp.window.float({action = 'enable', window = 'class:c'})")); + + // establish focus history + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); + + // kill the floating window + // Expect the focus to fall back to the left tiled window + OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:c'})")); + Tests::waitUntilWindowsN(2); + + // The focus now must have fallen back to tiled window of class "a". + + // If the view did not move, we expect currently focused window's (class:a) to have "at: " x coordinat value <0 (must be left of the viewport) + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) { + + // same idea as testScrollingViewBehaviourFocusFallback, but with window of class "a" being grouped. + + NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a grouped tiled should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + + // to correctly set up windows for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + // only one tiled window will be grouped for the test + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + // make it a grouped. There need not be any other windows in the group for this test + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with class `c`", Colors::RED); + } + + // make it float - the view now mush have shifted to fit window class:b + OK(getFromSocket("/dispatch hl.dsp.window.float({action = 'enable', window = 'class:c'})")); + + // establish focus history + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); + + // kill the floating window + OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:c'})")); + Tests::waitUntilWindowsN(2); + + // The focus now must have fallen back to tiled window of class "a". + + // If the view did not move, we expect currently focused window's (class:a) to have "at: " x coordinat value <0 (must be left of the viewport) + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { + + /* + When you change to a scrolling workspace, the focused window in that workspace must not be pulled into view, regardless of follow_focus + --------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: changing to a scrolling workspace should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + // switch to workspace 1 for this test + OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // change to workspace 2, then back to workspace 1 again + OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '2'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})")); + + // If the scrolling view did not move, the x value for `at:` of the currently focused window, class:a, must be <0 (must be left of the viewport) + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) { + + /* + When you change to a special scrolling workspace from a normal workspace, the focused window in that workspace must not be pulled into view, regardless of follow_focus + ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: changing to a special scrolling workspace from a normal workspace should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + // We'll test in this special workspace + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // change to workspace 2, then back to special "scroll_S" workspace again + OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '2'})")); + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); + + // Reestablish focus since it is finnicky in hyprtester - Harmless and does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourSpecialToSpecialWorkspaceChange) { + + /* + We also test switching between 2 special workspaces + This follows the same idea and dependencies as the test testScrollingViewBehaviourSpecialWorkspaceChange() + */ + + NLog::log("{}Testing scrolling view behaviour: changing to a special scrolling workspace from another special workspace should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + // We'll test in this special workspace + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // change to special workspace "scroll_F", then back to special "scroll_S" workspace again + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_F')")); + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); + + // Reestablish focus since it is finnicky in hyprtester - Harmless and does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) + + const std::string currentWindowPosSPECIAL = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosSPECIALX = currentWindowPosSPECIAL.substr(0, currentWindowPosSPECIAL.find(',')); + // test pass + if (std::stoi(currentWindowPosSPECIALX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosSPECIALX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosSPECIALX); + } +} + +TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) { + + /* + When you change close a window inside a group (NOT destroying the group!), it should not cause scrolling view to shift to pull that group into view, regardless of follow_focus + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: closing a window in a group (> 1 window in group) should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + + // this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + // We need 2 windows to be grouped, the third one not. + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // switch focus to group. This will not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // kill window class:b. we expect that this should cause not difference in the position of the group + OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:b'})")); + Tests::waitUntilWindowsN(2); + + // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:a, must be <0 (must be left of the viewport) + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourMoveWindowIntoGroupFollowFocusFalse) { + + /* + when a window is moved inside a group, scrolling view should not move to fit that group when follow_focus = false + ----------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: moving a window into a group SHOULD NOT move scrolling view if follow_focus = 0", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // focus class:b + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // move it into the group where class:a is + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + // the focus now should still be on class:b window. If the view did not move, its x coordinate for its `at:` value should be <0 + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'b' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'b' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourMoveWindowInGroupFollowFocusTrue) { + + /* + when a window is moved inside a group, scrolling view should move to fit that group when follow_focus = true + ------------------------------------------------------------------------------------------------------------ + */ + + NLog::log("{}Testing scrolling view behaviour: moving a window in a group SHOULD move scrolling view if follow_focus = true", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // focus class:b + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // move it into the group where class:a is + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + // the focus now should still be on class:b window. If the scrolling view did move, its x coordinate for its `at:` value should be >= 0 + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test fail + if (std::stoi(currentWindowPosX) < 0) { + FAIL_TEST("{}window of class 'b' does not have x coordinates >= 0 for its position: {}", Colors::RED, currentWindowPosX); + } + // test pass + else { + NLog ::log("{}Passed: {}window of class 'b' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourNewLayer) { + + /* + Starting a program on a different layer shouldn't cause scrolling view to move to fit the window that was focused when this program was started, regardless of follow_focus + --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: new program occupying another layer shouldn't move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // focus class:a - this does not move scrolling view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, "myLayer"); + if (!Tests::spawnLayerKitty("myLayer")) { + FAIL_TEST("{}Error: {} layer did not spawn", Colors::RED, "myLayer"); + } + + // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // TEST_CASE's own cleanup functions fail to kill all layers with this test. Manually do it + + // kill all layers + NLog::log("{}Killing all layers", Colors::YELLOW); + Tests::killAllLayers(); + ASSERT(Tests::layerCount(), 0); + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); +} + +TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusFalse) { + + /* + dispatching movefocus when follow_focus = false should not cause scrolling view to move + --------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: movefocus does not cause scrolling view to move if follow_focus = false", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // we expect that after dispatching this, scrolling view must not have moved + OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); + + // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be < 0. + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusTrue) { + + /* + dispatching movefocus when follow_focus = true should cause scrolling view to move + ---------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: movefocus does cause scrolling view to move if follow_focus = true", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // we expect that after dispatching this, scrolling view must have moved since follow_focus = true + OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); + + // If the scrolling view moved, class:a window's x coordinate for its `at:` value should be >= 0 + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test fail + if (std::stoi(currentWindowPosX) < 0) { + FAIL_TEST("{}Failed: {}window of class 'a' does not have x coordinates >= 0 for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + // test pass + else { + NLog ::log("{}Passed: {}window of class 'a' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusFalse) { + + /* + When movefocus is dispatched within groups to move focus from one group member to another, scrolling view must not move if follow_focus = false + ----------------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: movefocus within groups does not cause scrolling view to move if follow_focus = false", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + + // necessary to make sure movefocus first cycles through tabs in a group + OK(getFromSocket("/eval hl.config({ binds = {movefocus_cycles_groupfirst = true}})")); + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // focus class:b. This does not cause scrolling view to move when follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // move it into the group where class:a is. This does not cause scrolling view to move when follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + // we move from one window of a group to another (from class:b to class:a) via movefocus + OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); + + // the focus now should still be on class:a window. If the scrolling view did not move, its x coordinate for its `at:` value should be < 0 + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } +} + +TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue) { + + /* + When movefocus is dispatched within groups to move focus from one group member to another, scrolling view must move if follow_focus = true + ------------------------------------------------------------------------------------------------------------------------------------------ + */ + + NLog::log("{}Testing scrolling view behaviour: movefocus within groups does causes scrolling view to move if follow_focus = true", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + + // necessary to make sure movefocus first cycles through tabs in a group + OK(getFromSocket("/eval hl.config({ binds = {movefocus_cycles_groupfirst = true}})")); + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // focus class:b. This does not cause scrolling view to move when follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // move it into the group where class:a is. This does not cause scrolling view to move when follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + // we move from one window of a group to another (from class:b to class:a) via movefocus + OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); + + // the focus now should still be on class:a window. If the scrolling view moved, its x coordinate for its `at:` value should be >= 0 + + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); + // test fail + if (std::stoi(currentWindowPosX) < 0) { + FAIL_TEST("{}Failed: {}window of class 'a' does not have x coordinates >= 0 for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + // test pass + else { + NLog ::log("{}Passed: {}window of class 'a' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } +} |
