From 97f55741ad6a75d651449a4be37158e06cef439e Mon Sep 17 00:00:00 2001 From: Artha Date: Fri, 17 Jan 2025 06:43:48 +0100 Subject: [PATCH] Migrate tests for fluid.actions --- source/fluid/actions.d | 11 ++--- source/fluid/test_space.d | 58 +++++++++++++++---------- tests/actions/scroll_into_view_action.d | 56 ++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 tests/actions/scroll_into_view_action.d diff --git a/source/fluid/actions.d b/source/fluid/actions.d index df3009b..98b01f4 100644 --- a/source/fluid/actions.d +++ b/source/fluid/actions.d @@ -89,7 +89,7 @@ abstract class FocusSearchAction : NodeSearchAction, Publisher!Focusable { } /// Set focus on the given node, if focusable, or the first of its focusable children. This will be done lazily during -/// the next draw. +/// the next draw. /// /// If focusing the given node is not desired, use `focusRecurseChildren`. /// @@ -137,7 +137,7 @@ unittest { } -/// Set focus on the first of the node's focusable children. This will be done lazily during the next draw. +/// Set focus on the first of the node's focusable children. This will be done lazily during the next draw. /// /// Params: /// parent = Container node to search in. @@ -158,28 +158,24 @@ FocusRecurseAction focusChild(Node parent) { } +@("FocusRecurse works") unittest { import fluid.space; import fluid.button; - auto io = new HeadlessBackend; auto root = vframeButton( button("", delegate { }), button("", delegate { }), delegate { } ); - root.io = io; - // Typical focusRecurse call will focus the button root.focusRecurse; root.draw(); assert(root.tree.focus is root); - io.nextFrame; - // If we want to make sure the action descends below the root, we must root.focusRecurseChildren; root.draw(); @@ -252,6 +248,7 @@ ScrollIntoViewAction scrollToTop(Node node) { } +@("Legacy: ScrollIntoViewAction works (migrated)") unittest { import fluid; diff --git a/source/fluid/test_space.d b/source/fluid/test_space.d index f2f3702..b215e78 100644 --- a/source/fluid/test_space.d +++ b/source/fluid/test_space.d @@ -747,8 +747,10 @@ auto drawsImage(Node subject) { bool isTestingImage; Image targetImage; - bool isTestingArea; - Rectangle targetArea; + bool isTestingStart; + Vector2 targetStart; + bool isTestingSize; + Vector2 targetSize; bool isTestingColor; Color targetColor; bool isTestingHint; @@ -776,12 +778,18 @@ auto drawsImage(Node subject) { } } - if (isTestingArea) { - assert(equal(targetArea.x, rect.x) - && equal(targetArea.y, rect.y) - && equal(targetArea.width, rect.width) - && equal(targetArea.height, rect.height), - format!"%s should draw image at %s, but draws at %s"(node, targetArea, rect).assertNotThrown); + if (isTestingStart) { + assert(equal(targetStart.x, rect.x) + && equal(targetStart.y, rect.y), + format!"%s should draw image at %s, but draws at %s"(node, targetStart, rect.start) + .assertNotThrown); + } + + if (isTestingSize) { + assert(equal(targetSize.x, rect.w) + && equal(targetSize.y, rect.h), + format!"%s should draw image of size %s, but draws %s"(node, targetSize, rect.size) + .assertNotThrown); } if (isTestingColor) { @@ -806,33 +814,28 @@ auto drawsImage(Node subject) { } typeof(this) at(Vector2 position) @safe { - isTestingArea = true; - targetArea = Rectangle(position.tupleof, targetImage.size.tupleof); + isTestingStart = true; + targetStart = position; // TODO DPI return this; } typeof(this) at(typeof(Vector2.tupleof) position) @safe { - isTestingArea = true; - targetArea = Rectangle(position, targetImage.size.tupleof); - // TODO DPI + return at(Vector2(position)); return this; } typeof(this) at(Rectangle area) @safe { - isTestingArea = true; - targetArea = area; - // TODO DPI + at(area.start); + isTestingSize = true; + targetSize = area.size; return this; } - typeof(this) at(typeof(Rectangle.tupleof) position) @safe { - isTestingArea = true; - targetArea = Rectangle(position); - // TODO DPI - return this; + typeof(this) at(typeof(Rectangle.tupleof) area) @safe { + return at(Rectangle(area)); } typeof(this) withPalette(Color[] colors...) @safe { @@ -855,7 +858,8 @@ auto drawsImage(Node subject) { return toText( subject, " should draw an image ", isTestingImage ? toText(targetImage) : "", - isTestingArea ? toText(" rectangle ", targetArea) : "", + isTestingStart ? toText(" at ", targetStart) : "", + isTestingSize ? toText(" of size ", targetSize) : "", isTestingColor ? toText(" of color ", targetColor.toHex) : "", ); } @@ -1073,11 +1077,15 @@ auto draws(Node subject) { } /// Make sure the selected node doesn't draw anything until another node does. -auto doesNotDraw(Node subject) { +auto doesNotDraw(alias predicate = `a.startsWith("draw")`)(Node subject) { + + import std.functional : unaryFun; bool matched; string failedName; + alias fun = unaryFun!predicate; + return drawsWildcard!((node, methodName) { // Test failed, skip checks @@ -1103,7 +1111,7 @@ auto doesNotDraw(Node subject) { return true; } - if (isSubject && methodName.startsWith("draw")) { + if (isSubject && fun(methodName)) { failedName = methodName; return false; } @@ -1115,6 +1123,8 @@ auto doesNotDraw(Node subject) { } +alias doesNotDrawImages = doesNotDraw!`a.among("drawImage", "drawHintedImage")`; + /// Ensure the node emits a debug signal. auto emits(Node subject, string name) { diff --git a/tests/actions/scroll_into_view_action.d b/tests/actions/scroll_into_view_action.d new file mode 100644 index 0000000..aabf88a --- /dev/null +++ b/tests/actions/scroll_into_view_action.d @@ -0,0 +1,56 @@ +module actions.scroll_into_view_action; + +import std.math; +import std.array; +import std.range; +import std.algorithm; + +import fluid; + +@safe: + +@("ScrollIntoViewAction works") +unittest { + + const viewportHeight = 10; + + Label[3] labels; + + auto frame = vscrollFrame( + .layout!(1, "fill"), + labels[0] = label("a"), + labels[1] = label("b"), + labels[2] = label("c"), + ); + auto root = sizeLock!testSpace( + .nullTheme, + .sizeLimit(10, viewportHeight), + .cropViewport, + frame + ); + + frame.scrollBar.width = 0; // TODO replace this with scrollBar.hide() + + // Prepare scrolling + // Note: Changes made when scrolling will be visible during the next frame + frame.children[1].scrollIntoView; + root.draw(); + + // No theme so everything is as compact as it can be: the first label should be at the very top + // It is reasonable to assume the text will be larger than 10 pixels (viewport height) + // Other text will not render, since it's offscreen + root.drawAndAssert( + labels[0].doesNotDrawImages(), + labels[1].drawsImage().at(0, viewportHeight - labels[1].text.size.y), + labels[2].doesNotDrawImages(), + ); + // TODO Because the label was hidden below the viewport, Fluid will align the bottom of the selected node with the + // viewport which probably isn't appropriate in case *like this* where it should reveal the top of the node. + + // auto texture1 = io.textures.front; + // assert(isClose(texture1.position.y + texture1.height, viewportHeight)); + assert(isClose(frame.scroll, (frame.scrollMax + 10) * 2/3 - 10)); + + // TODO more tests. Scrolling while already in the viewport, scrolling while partially out of the view, etc. + +}