#include #include "XCUIBackend/XCUILayoutLabRuntime.h" #include #include #include namespace { using XCEngine::UI::UIDrawCommand; using XCEngine::UI::UIDrawCommandType; XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState BuildInputState( float width = 960.0f, float height = 640.0f) { XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState input = {}; input.canvasRect = XCEngine::UI::UIRect(0.0f, 0.0f, width, height); input.pointerPosition = XCEngine::UI::UIPoint(width * 0.5f, height * 0.5f); input.pointerInside = true; return input; } std::vector CollectTextCommands(const XCEngine::UI::UIDrawData& drawData) { std::vector textCommands = {}; for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) { for (const UIDrawCommand& command : drawList.GetCommands()) { if (command.type == UIDrawCommandType::Text) { textCommands.push_back(&command); } } } return textCommands; } const UIDrawCommand* FindTextCommand( const XCEngine::UI::UIDrawData& drawData, const std::string& text) { for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) { for (const UIDrawCommand& command : drawList.GetCommands()) { if (command.type == UIDrawCommandType::Text && command.text == text) { return &command; } } } return nullptr; } std::size_t CountCommandsOfType( const XCEngine::UI::UIDrawData& drawData, UIDrawCommandType type) { std::size_t count = 0; for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) { for (const UIDrawCommand& command : drawList.GetCommands()) { if (command.type == type) { ++count; } } } return count; } } // namespace TEST(NewEditorXCUILayoutLabRuntimeTest, UpdateBuildsLayoutSmokeFrame) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; const bool reloadSucceeded = runtime.ReloadDocuments(); const auto& frame = runtime.Update(BuildInputState()); EXPECT_EQ(frame.stats.documentsReady, reloadSucceeded); EXPECT_EQ(frame.stats.drawListCount, frame.drawData.GetDrawListCount()); EXPECT_EQ(frame.stats.commandCount, frame.drawData.GetTotalCommandCount()); if (frame.stats.documentsReady) { EXPECT_GT(frame.stats.drawListCount, 0u); EXPECT_GT(frame.stats.commandCount, 0u); EXPECT_GE(frame.stats.rowCount, 1u); EXPECT_GE(frame.stats.columnCount, 1u); EXPECT_GE(frame.stats.overlayCount, 1u); EXPECT_GE(frame.stats.scrollViewCount, 2u); EXPECT_GE(frame.stats.treeViewCount, 1u); EXPECT_GE(frame.stats.treeItemCount, 3u); EXPECT_GE(frame.stats.listViewCount, 1u); EXPECT_GE(frame.stats.listItemCount, 3u); EXPECT_GE(frame.stats.propertySectionCount, 2u); EXPECT_GE(frame.stats.fieldRowCount, 4u); XCEngine::UI::UIRect heroRect = {}; EXPECT_TRUE(runtime.TryGetElementRect("heroCard", heroRect)); EXPECT_GT(heroRect.width, 0.0f); EXPECT_GT(heroRect.height, 0.0f); } else { EXPECT_FALSE(frame.stats.statusMessage.empty()); } } TEST(NewEditorXCUILayoutLabRuntimeTest, FrameIncludesTextCommandsWithThemeFontSizes) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& frame = runtime.Update(BuildInputState()); ASSERT_TRUE(frame.stats.documentsReady); const std::vector textCommands = CollectTextCommands(frame.drawData); ASSERT_FALSE(textCommands.empty()); for (const UIDrawCommand* command : textCommands) { ASSERT_NE(command, nullptr); EXPECT_FALSE(command->text.empty()); EXPECT_GT(command->fontSize, 0.0f); } const UIDrawCommand* titleCommand = FindTextCommand(frame.drawData, "XCUI Layout Lab"); ASSERT_NE(titleCommand, nullptr); EXPECT_FLOAT_EQ(titleCommand->fontSize, 16.0f); const UIDrawCommand* subtitleCommand = FindTextCommand( frame.drawData, "Editor-style panels with overlay and scroll semantics."); ASSERT_NE(subtitleCommand, nullptr); EXPECT_FLOAT_EQ(subtitleCommand->fontSize, 13.0f); } TEST(NewEditorXCUILayoutLabRuntimeTest, HoverProbeResolvesTrackedElementRect) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& baseline = runtime.Update(BuildInputState()); ASSERT_TRUE(baseline.stats.documentsReady); XCEngine::UI::UIRect probeRect = {}; ASSERT_TRUE(runtime.TryGetElementRect("assetLighting", probeRect)); ASSERT_GT(probeRect.width, 0.0f); ASSERT_GT(probeRect.height, 0.0f); XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState input = BuildInputState(); input.pointerPosition = XCEngine::UI::UIPoint( probeRect.x + probeRect.width * 0.5f, probeRect.y + probeRect.height * 0.5f); const auto& frame = runtime.Update(input); ASSERT_TRUE(frame.stats.documentsReady); EXPECT_FALSE(frame.stats.hoveredElementId.empty()); XCEngine::UI::UIRect hoveredRect = {}; EXPECT_TRUE(runtime.TryGetElementRect(frame.stats.hoveredElementId, hoveredRect)); EXPECT_GT(hoveredRect.width, 0.0f); EXPECT_GT(hoveredRect.height, 0.0f); } TEST(NewEditorXCUILayoutLabRuntimeTest, EditorPrototypeWidgetsExposeRectsAndLabels) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& frame = runtime.Update(BuildInputState()); ASSERT_TRUE(frame.stats.documentsReady); XCEngine::UI::UIRect projectTreeRect = {}; XCEngine::UI::UIRect fieldPositionRect = {}; ASSERT_TRUE(runtime.TryGetElementRect("projectTree", projectTreeRect)); ASSERT_TRUE(runtime.TryGetElementRect("fieldPosition", fieldPositionRect)); EXPECT_GT(projectTreeRect.height, 0.0f); EXPECT_GT(fieldPositionRect.width, 0.0f); EXPECT_NE(FindTextCommand(frame.drawData, "Assets"), nullptr); EXPECT_NE(FindTextCommand(frame.drawData, "Lighting_GlobalRig"), nullptr); EXPECT_NE(FindTextCommand(frame.drawData, "Position"), nullptr); EXPECT_NE(FindTextCommand(frame.drawData, "0.0, 1.5, 0.0"), nullptr); } TEST(NewEditorXCUILayoutLabRuntimeTest, ScrollViewOffsetsContentAndAddsNestedClips) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& frame = runtime.Update(BuildInputState()); ASSERT_TRUE(frame.stats.documentsReady); XCEngine::UI::UIRect assetListRect = {}; XCEngine::UI::UIRect headerRect = {}; XCEngine::UI::UIRect visibleItemRect = {}; ASSERT_TRUE(runtime.TryGetElementRect("assetList", assetListRect)); ASSERT_TRUE(runtime.TryGetElementRect("assetListHeader", headerRect)); ASSERT_TRUE(runtime.TryGetElementRect("assetLighting", visibleItemRect)); EXPECT_LT(headerRect.y, assetListRect.y); EXPECT_GT(visibleItemRect.y, assetListRect.y); EXPECT_LT(visibleItemRect.y, assetListRect.y + assetListRect.height); EXPECT_EQ( CountCommandsOfType(frame.drawData, UIDrawCommandType::PushClipRect), frame.stats.clipPushCommandCount); EXPECT_EQ( CountCommandsOfType(frame.drawData, UIDrawCommandType::PopClipRect), frame.stats.clipPopCommandCount); EXPECT_GE(frame.stats.clipPushCommandCount, 3u); EXPECT_GE(frame.stats.clipPopCommandCount, 3u); } TEST(NewEditorXCUILayoutLabRuntimeTest, HoverIgnoresClippedScrollViewContent) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& baseline = runtime.Update(BuildInputState()); ASSERT_TRUE(baseline.stats.documentsReady); XCEngine::UI::UIRect assetListRect = {}; ASSERT_TRUE(runtime.TryGetElementRect("assetList", assetListRect)); XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState input = BuildInputState(); input.pointerPosition = XCEngine::UI::UIPoint( assetListRect.x + assetListRect.width * 0.5f, assetListRect.y + assetListRect.height + 6.0f); const auto& frame = runtime.Update(input); ASSERT_TRUE(frame.stats.documentsReady); EXPECT_TRUE(frame.stats.hoveredElementId.empty()); } TEST(NewEditorXCUILayoutLabRuntimeTest, ClickSelectionPersistsOnSharedCollectionItems) { XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime runtime; ASSERT_TRUE(runtime.ReloadDocuments()); const auto& baseline = runtime.Update(BuildInputState()); ASSERT_TRUE(baseline.stats.documentsReady); XCEngine::UI::UIRect targetRect = {}; ASSERT_TRUE(runtime.TryGetElementRect("fieldPosition", targetRect)); XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState clickInput = BuildInputState(); clickInput.pointerPosition = XCEngine::UI::UIPoint( targetRect.x + targetRect.width * 0.5f, targetRect.y + targetRect.height * 0.5f); clickInput.pointerPressed = true; const auto& selectedFrame = runtime.Update(clickInput); ASSERT_TRUE(selectedFrame.stats.documentsReady); ASSERT_FALSE(selectedFrame.stats.hoveredElementId.empty()); EXPECT_EQ( selectedFrame.stats.selectedElementId, selectedFrame.stats.hoveredElementId); const std::string selectedElementId = selectedFrame.stats.selectedElementId; XCEngine::UI::UIRect selectedRect = {}; EXPECT_TRUE(runtime.TryGetElementRect(selectedElementId, selectedRect)); EXPECT_GT(selectedRect.width, 0.0f); EXPECT_GT(selectedRect.height, 0.0f); const auto& persistedFrame = runtime.Update(BuildInputState()); ASSERT_TRUE(persistedFrame.stats.documentsReady); EXPECT_EQ(persistedFrame.stats.selectedElementId, selectedElementId); }