Add XCUI expansion state and coverage tests
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
set(UI_TEST_SOURCES
|
||||
test_ui_core.cpp
|
||||
test_ui_editor_collection_primitives.cpp
|
||||
test_ui_expansion_model.cpp
|
||||
test_layout_engine.cpp
|
||||
test_ui_selection_model.cpp
|
||||
test_ui_runtime.cpp
|
||||
|
||||
@@ -50,6 +50,7 @@ TEST(UIEditorCollectionPrimitivesTest, ClassifyAndFlagsMatchEditorCollectionTags
|
||||
|
||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::TreeItem));
|
||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::ListItem));
|
||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::PropertySection));
|
||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::FieldRow));
|
||||
EXPECT_FALSE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::TreeView));
|
||||
}
|
||||
|
||||
45
tests/Core/UI/test_ui_expansion_model.cpp
Normal file
45
tests/Core/UI/test_ui_expansion_model.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::UI::Widgets::UIExpansionModel;
|
||||
|
||||
TEST(UIExpansionModelTest, ExpandCollapseAndClearTrackExpandedItems) {
|
||||
UIExpansionModel expansion = {};
|
||||
|
||||
EXPECT_FALSE(expansion.HasExpandedItems());
|
||||
EXPECT_EQ(expansion.GetExpandedCount(), 0u);
|
||||
|
||||
EXPECT_TRUE(expansion.Expand("treeAssetsRoot"));
|
||||
EXPECT_TRUE(expansion.IsExpanded("treeAssetsRoot"));
|
||||
EXPECT_TRUE(expansion.HasExpandedItems());
|
||||
EXPECT_EQ(expansion.GetExpandedCount(), 1u);
|
||||
|
||||
EXPECT_FALSE(expansion.Expand("treeAssetsRoot"));
|
||||
EXPECT_TRUE(expansion.Collapse("treeAssetsRoot"));
|
||||
EXPECT_FALSE(expansion.IsExpanded("treeAssetsRoot"));
|
||||
EXPECT_EQ(expansion.GetExpandedCount(), 0u);
|
||||
EXPECT_FALSE(expansion.Collapse("treeAssetsRoot"));
|
||||
EXPECT_FALSE(expansion.Clear());
|
||||
}
|
||||
|
||||
TEST(UIExpansionModelTest, SetAndToggleExpandedReplaceStateForMatchingItem) {
|
||||
UIExpansionModel expansion = {};
|
||||
|
||||
EXPECT_TRUE(expansion.SetExpanded("inspectorTransform", true));
|
||||
EXPECT_TRUE(expansion.IsExpanded("inspectorTransform"));
|
||||
EXPECT_EQ(expansion.GetExpandedCount(), 1u);
|
||||
|
||||
EXPECT_FALSE(expansion.SetExpanded("inspectorTransform", true));
|
||||
EXPECT_TRUE(expansion.ToggleExpanded("inspectorTransform"));
|
||||
EXPECT_FALSE(expansion.IsExpanded("inspectorTransform"));
|
||||
|
||||
EXPECT_TRUE(expansion.ToggleExpanded("inspectorMesh"));
|
||||
EXPECT_TRUE(expansion.IsExpanded("inspectorMesh"));
|
||||
EXPECT_TRUE(expansion.SetExpanded("inspectorMesh", false));
|
||||
EXPECT_FALSE(expansion.IsExpanded("inspectorMesh"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -41,6 +41,50 @@ TEST(UITextInputControllerTest, BackspaceAndArrowKeysUseUtf8Boundaries) {
|
||||
EXPECT_EQ(state.caret, 1u);
|
||||
}
|
||||
|
||||
TEST(UITextInputControllerTest, DeleteUsesUtf8BoundariesAndLeavesCaretAtDeletePoint) {
|
||||
if (static_cast<std::int32_t>(KeyCode::Delete) ==
|
||||
static_cast<std::int32_t>(KeyCode::Backspace)) {
|
||||
GTEST_SKIP() << "KeyCode::Delete currently aliases Backspace.";
|
||||
}
|
||||
|
||||
UIText::UITextInputState state = {};
|
||||
state.value = std::string("A") + "\xE4\xBD\xA0" + "B";
|
||||
state.caret = 1u;
|
||||
|
||||
const auto result = UIText::HandleKeyDown(
|
||||
state,
|
||||
static_cast<std::int32_t>(KeyCode::Delete),
|
||||
{},
|
||||
{});
|
||||
EXPECT_TRUE(result.handled);
|
||||
EXPECT_TRUE(result.valueChanged);
|
||||
EXPECT_FALSE(result.submitRequested);
|
||||
EXPECT_EQ(state.value, "AB");
|
||||
EXPECT_EQ(state.caret, 1u);
|
||||
}
|
||||
|
||||
TEST(UITextInputControllerTest, DeleteClampsOversizedCaretAndDoesNotMutateAtDocumentEnd) {
|
||||
if (static_cast<std::int32_t>(KeyCode::Delete) ==
|
||||
static_cast<std::int32_t>(KeyCode::Backspace)) {
|
||||
GTEST_SKIP() << "KeyCode::Delete currently aliases Backspace.";
|
||||
}
|
||||
|
||||
UIText::UITextInputState state = {};
|
||||
state.value = "AB";
|
||||
state.caret = 99u;
|
||||
|
||||
const auto result = UIText::HandleKeyDown(
|
||||
state,
|
||||
static_cast<std::int32_t>(KeyCode::Delete),
|
||||
{},
|
||||
{});
|
||||
EXPECT_TRUE(result.handled);
|
||||
EXPECT_FALSE(result.valueChanged);
|
||||
EXPECT_FALSE(result.submitRequested);
|
||||
EXPECT_EQ(state.value, "AB");
|
||||
EXPECT_EQ(state.caret, state.value.size());
|
||||
}
|
||||
|
||||
TEST(UITextInputControllerTest, SingleLineEnterRequestsSubmitWithoutMutatingValue) {
|
||||
UIText::UITextInputState state = {};
|
||||
state.value = "prompt";
|
||||
@@ -82,6 +126,64 @@ TEST(UITextInputControllerTest, MultilineEnterAndVerticalMovementStayInControlle
|
||||
EXPECT_EQ(state.value, std::string("A") + "\xE4\xBD\xA0" + "Z\nBC\n");
|
||||
}
|
||||
|
||||
TEST(UITextInputControllerTest, HomeAndEndRespectSingleLineAndMultilineBounds) {
|
||||
UIText::UITextInputState singleLine = {};
|
||||
singleLine.value = "prompt";
|
||||
singleLine.caret = 2u;
|
||||
|
||||
const auto singleHome = UIText::HandleKeyDown(
|
||||
singleLine,
|
||||
static_cast<std::int32_t>(KeyCode::Home),
|
||||
{},
|
||||
{});
|
||||
EXPECT_TRUE(singleHome.handled);
|
||||
EXPECT_EQ(singleLine.caret, 0u);
|
||||
|
||||
const auto singleEnd = UIText::HandleKeyDown(
|
||||
singleLine,
|
||||
static_cast<std::int32_t>(KeyCode::End),
|
||||
{},
|
||||
{});
|
||||
EXPECT_TRUE(singleEnd.handled);
|
||||
EXPECT_EQ(singleLine.caret, singleLine.value.size());
|
||||
|
||||
UIText::UITextInputState multiline = {};
|
||||
multiline.value = "root\nleaf\nend";
|
||||
multiline.caret = 7u;
|
||||
const UIText::UITextInputOptions options = { true, 4u };
|
||||
|
||||
const auto multilineHome = UIText::HandleKeyDown(
|
||||
multiline,
|
||||
static_cast<std::int32_t>(KeyCode::Home),
|
||||
{},
|
||||
options);
|
||||
EXPECT_TRUE(multilineHome.handled);
|
||||
EXPECT_EQ(multiline.caret, 5u);
|
||||
|
||||
multiline.caret = 7u;
|
||||
const auto multilineEnd = UIText::HandleKeyDown(
|
||||
multiline,
|
||||
static_cast<std::int32_t>(KeyCode::End),
|
||||
{},
|
||||
options);
|
||||
EXPECT_TRUE(multilineEnd.handled);
|
||||
EXPECT_EQ(multiline.caret, 9u);
|
||||
}
|
||||
|
||||
TEST(UITextInputControllerTest, ClampCaretAndInsertCharacterRecoverFromOversizedCaret) {
|
||||
UIText::UITextInputState state = {};
|
||||
state.value = "go";
|
||||
state.caret = 42u;
|
||||
|
||||
UIText::ClampCaret(state);
|
||||
EXPECT_EQ(state.caret, state.value.size());
|
||||
|
||||
state.caret = 42u;
|
||||
EXPECT_TRUE(UIText::InsertCharacter(state, '!'));
|
||||
EXPECT_EQ(state.value, "go!");
|
||||
EXPECT_EQ(state.caret, state.value.size());
|
||||
}
|
||||
|
||||
TEST(UITextInputControllerTest, MultilineTabAndShiftTabIndentAndOutdentCurrentLine) {
|
||||
UIText::UITextInputState state = {};
|
||||
state.value = "root\nnode";
|
||||
@@ -112,4 +214,74 @@ TEST(UITextInputControllerTest, MultilineTabAndShiftTabIndentAndOutdentCurrentLi
|
||||
EXPECT_EQ(state.caret, 5u);
|
||||
}
|
||||
|
||||
TEST(UITextInputControllerTest, ShiftTabWithoutLeadingSpacesIsHandledWithoutMutatingText) {
|
||||
UIText::UITextInputState state = {};
|
||||
state.value = "root\nnode";
|
||||
state.caret = 5u;
|
||||
|
||||
XCEngine::UI::UIInputModifiers modifiers = {};
|
||||
modifiers.shift = true;
|
||||
|
||||
const auto result = UIText::HandleKeyDown(
|
||||
state,
|
||||
static_cast<std::int32_t>(KeyCode::Tab),
|
||||
modifiers,
|
||||
{ true, 4u });
|
||||
EXPECT_TRUE(result.handled);
|
||||
EXPECT_FALSE(result.valueChanged);
|
||||
EXPECT_FALSE(result.submitRequested);
|
||||
EXPECT_EQ(state.value, "root\nnode");
|
||||
EXPECT_EQ(state.caret, 5u);
|
||||
}
|
||||
|
||||
TEST(UITextInputControllerTest, MultilineTabIgnoresSystemModifiers) {
|
||||
const auto buildState = []() {
|
||||
UIText::UITextInputState state = {};
|
||||
state.value = "root\nnode";
|
||||
state.caret = 5u;
|
||||
return state;
|
||||
};
|
||||
|
||||
const UIText::UITextInputOptions options = { true, 4u };
|
||||
|
||||
XCEngine::UI::UIInputModifiers control = {};
|
||||
control.control = true;
|
||||
auto controlState = buildState();
|
||||
const auto controlResult = UIText::HandleKeyDown(
|
||||
controlState,
|
||||
static_cast<std::int32_t>(KeyCode::Tab),
|
||||
control,
|
||||
options);
|
||||
EXPECT_FALSE(controlResult.handled);
|
||||
EXPECT_FALSE(controlResult.valueChanged);
|
||||
EXPECT_EQ(controlState.value, "root\nnode");
|
||||
EXPECT_EQ(controlState.caret, 5u);
|
||||
|
||||
XCEngine::UI::UIInputModifiers alt = {};
|
||||
alt.alt = true;
|
||||
auto altState = buildState();
|
||||
const auto altResult = UIText::HandleKeyDown(
|
||||
altState,
|
||||
static_cast<std::int32_t>(KeyCode::Tab),
|
||||
alt,
|
||||
options);
|
||||
EXPECT_FALSE(altResult.handled);
|
||||
EXPECT_FALSE(altResult.valueChanged);
|
||||
EXPECT_EQ(altState.value, "root\nnode");
|
||||
EXPECT_EQ(altState.caret, 5u);
|
||||
|
||||
XCEngine::UI::UIInputModifiers superModifier = {};
|
||||
superModifier.super = true;
|
||||
auto superState = buildState();
|
||||
const auto superResult = UIText::HandleKeyDown(
|
||||
superState,
|
||||
static_cast<std::int32_t>(KeyCode::Tab),
|
||||
superModifier,
|
||||
options);
|
||||
EXPECT_FALSE(superResult.handled);
|
||||
EXPECT_FALSE(superResult.valueChanged);
|
||||
EXPECT_EQ(superState.value, "root\nnode");
|
||||
EXPECT_EQ(superState.caret, 5u);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user