AssetPicker_UI_UX.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. """
  6. def AssetPicker_UI_UX():
  7. import pyside_utils
  8. @pyside_utils.wrap_async
  9. async def run_test():
  10. """
  11. Summary:
  12. Verify the functionality of Asset Picker and UI/UX properties
  13. Expected Behavior:
  14. The asset picker opens and is labeled appropriately ("Pick Model Asset" in this instance).
  15. The Asset Picker window can be resized and moved around the screen.
  16. The file tree expands/retracts appropriately and a scroll bar is present when the menus extend
  17. beyond the length of the window.
  18. The assets are limited to a valid type for the field selected (model assets in this instance)
  19. The asset picker is closed and the selected asset is assigned to the mesh component.
  20. Test Steps:
  21. 1) Open a simple level
  22. 2) Create entity and add Mesh component
  23. 3) Access Entity Inspector
  24. 4) Click Asset Picker (Model Asset)
  25. a) Collapse all the files initially and verify if scroll bar is not visible
  26. b) Expand/Verify Top folder of file path
  27. c) Expand/Verify Nested folder of file path
  28. d) Verify if the ScrollBar appears after expanding folders
  29. e) Collapse Nested and Top Level folders and verify if collapsed
  30. f) Verify if the correct files are appearing in the Asset Picker
  31. g) Move the widget and verify position
  32. h) Resize the widget
  33. g) Assign Model asset
  34. 5) Verify if Model Asset is assigned via both OK/Enter options
  35. Note:
  36. - This test file must be called from the O3DE Editor command terminal
  37. - Any passed and failed tests are written to the Editor.log file.
  38. Parsing the file or running a log_monitor are required to observe the test results.
  39. :return: None
  40. """
  41. import os
  42. from PySide2 import QtWidgets, QtTest, QtCore
  43. from PySide2.QtCore import Qt
  44. import azlmbr.asset as asset
  45. import azlmbr.bus as bus
  46. import azlmbr.legacy.general as general
  47. import azlmbr.math as math
  48. import editor_python_test_tools.hydra_editor_utils as hydra
  49. from editor_python_test_tools.utils import Report
  50. from editor_python_test_tools.utils import TestHelper as helper
  51. file_path = ["AutomatedTesting", "Assets", "Objects", "Foliage"]
  52. def is_asset_assigned(component, interaction_option):
  53. path = os.path.join("assets", "objects", "foliage", "cedar.azmodel")
  54. expected_asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, 'GetAssetIdByPath', path, math.Uuid(),
  55. False)
  56. result = hydra.get_component_property_value(component, "Controller|Configuration|Model Asset")
  57. expected_asset_str = expected_asset_id.invoke("ToString")
  58. result_str = result.invoke("ToString")
  59. Report.info(f"Asset assigned for {interaction_option} option: {expected_asset_str == result_str}")
  60. return expected_asset_str == result_str
  61. def move_and_resize_widget(widget):
  62. # Move the widget and verify position
  63. initial_position = widget.pos()
  64. x, y = initial_position.x() + 5, initial_position.y() + 5
  65. widget.move(x, y)
  66. curr_position = widget.pos()
  67. asset_picker_moved = (
  68. "Asset Picker widget moved successfully",
  69. "Failed to move Asset Picker widget"
  70. )
  71. Report.result(asset_picker_moved, curr_position.x() == x and curr_position.y() == y)
  72. # Resize the widget and verify size
  73. width, height = (
  74. widget.geometry().width() + 10,
  75. widget.geometry().height() + 10,
  76. )
  77. widget.resize(width, height)
  78. asset_picker_resized = (
  79. "Resized Asset Picker widget successfully",
  80. "Failed to resize Asset Picker widget"
  81. )
  82. Report.result(asset_picker_resized, widget.geometry().width() == width and widget.geometry().height() ==
  83. height)
  84. def verify_expand(model_index, tree):
  85. initially_collapsed = (
  86. "Folder initially collapsed",
  87. "Folder unexpectedly expanded"
  88. )
  89. expanded = (
  90. "Folder expanded successfully",
  91. "Failed to expand folder"
  92. )
  93. # Check initial collapse
  94. Report.result(initially_collapsed, not tree.isExpanded(model_index))
  95. # Expand at the specified index
  96. tree.expand(model_index)
  97. # Verify expansion
  98. Report.result(expanded, tree.isExpanded(model_index))
  99. def verify_collapse(model_index, tree):
  100. collapsed = (
  101. "Folder hierarchy collapsed successfully",
  102. "Failed to collapse folder hierarchy"
  103. )
  104. tree.collapse(model_index)
  105. Report.result(collapsed, not tree.isExpanded(model_index))
  106. def verify_files_appeared(model, allowed_asset_extensions, parent_index=QtCore.QModelIndex()):
  107. indices = [parent_index]
  108. while len(indices) > 0:
  109. parent_index = indices.pop(0)
  110. for row in range(model.rowCount(parent_index)):
  111. cur_index = model.index(row, 0, parent_index)
  112. cur_data = cur_index.data(Qt.DisplayRole)
  113. if (
  114. "." in cur_data
  115. and (cur_data.lower().split(".")[-1] not in allowed_asset_extensions)
  116. and not cur_data[-1] == ")"
  117. ):
  118. Report.info(f"Incorrect file found: {cur_data}")
  119. return False
  120. indices.append(cur_index)
  121. return True
  122. async def asset_picker(allowed_asset_extensions, asset, interaction_option):
  123. active_modal_widget = await pyside_utils.wait_for_modal_widget()
  124. if active_modal_widget:
  125. dialog = active_modal_widget.findChildren(QtWidgets.QDialog, "AssetPickerDialogClass")[0]
  126. asset_picker_title = (
  127. "Asset Picker window is titled as expected",
  128. "Asset Picker window has an unexpected title"
  129. )
  130. Report.result(asset_picker_title, dialog.windowTitle() == "Pick ModelAsset")
  131. tree = dialog.findChildren(QtWidgets.QTreeView, "m_assetBrowserTreeViewWidget")[0]
  132. scroll_area = tree.findChild(QtWidgets.QWidget, "qt_scrollarea_vcontainer")
  133. scroll_bar = scroll_area.findChild(QtWidgets.QScrollBar)
  134. # a) Collapse all the files initially and verify if scroll bar is not visible
  135. tree.collapseAll()
  136. await pyside_utils.wait_for_condition(lambda: not scroll_bar.isVisible(), 0.5)
  137. scroll_bar_hidden = (
  138. "Scroll Bar is not visible before tree expansion",
  139. "Scroll Bar is visible before tree expansion"
  140. )
  141. Report.result(scroll_bar_hidden, not scroll_bar.isVisible())
  142. # Get Model Index of the file paths
  143. model_index_1 = pyside_utils.find_child_by_pattern(tree, file_path[0])
  144. model_index_2 = pyside_utils.find_child_by_pattern(model_index_1, file_path[1])
  145. # b) Expand/Verify Top folder of file path
  146. verify_expand(model_index_1, tree)
  147. # c) Expand/Verify Nested folder of file path
  148. verify_expand(model_index_2, tree)
  149. # d) Verify if the ScrollBar appears after expanding folders
  150. tree.expandAll()
  151. await pyside_utils.wait_for_condition(lambda: scroll_bar.isVisible(), 0.5)
  152. scroll_bar_visible = (
  153. "Scroll Bar is visible after tree expansion",
  154. "Scroll Bar is not visible after tree expansion"
  155. )
  156. Report.result(scroll_bar_visible, scroll_bar.isVisible())
  157. # e) Collapse Nested and Top Level folders and verify if collapsed
  158. verify_collapse(model_index_2, tree)
  159. verify_collapse(model_index_1, tree)
  160. # f) Verify if the correct files are appearing in the Asset Picker
  161. asset_picker_correct_files_appear = (
  162. "Expected assets populated in the file picker",
  163. "Found unexpected assets in the file picker"
  164. )
  165. Report.result(asset_picker_correct_files_appear, verify_files_appeared(tree.model(),
  166. allowed_asset_extensions))
  167. # While we are here we can also check if we can resize and move the widget
  168. move_and_resize_widget(active_modal_widget)
  169. # g) Assign asset
  170. tree.collapseAll()
  171. await pyside_utils.wait_for_condition(
  172. lambda: len(dialog.findChildren(QtWidgets.QFrame, "m_searchWidget")) > 0, 0.5)
  173. search_widget = dialog.findChildren(QtWidgets.QFrame, "m_searchWidget")[0]
  174. search_line_edit = search_widget.findChildren(QtWidgets.QLineEdit, "textSearch")[0]
  175. search_line_edit.setText(asset)
  176. tree.expandAll()
  177. asset_model_index = pyside_utils.find_child_by_pattern(tree, asset)
  178. await pyside_utils.wait_for_condition(lambda: asset_model_index.isValid(), 2.0)
  179. tree.expand(asset_model_index)
  180. tree.setCurrentIndex(asset_model_index)
  181. if interaction_option == "ok":
  182. button_box = dialog.findChild(QtWidgets.QDialogButtonBox, "m_buttonBox")
  183. ok_button = button_box.button(QtWidgets.QDialogButtonBox.Ok)
  184. await pyside_utils.click_button_async(ok_button)
  185. elif interaction_option == "enter":
  186. QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier)
  187. # 1) Open an existing simple level
  188. hydra.open_base_level()
  189. # 2) Create entity and add Mesh component
  190. entity_position = math.Vector3(125.0, 136.0, 32.0)
  191. entity = hydra.Entity("TestEntity")
  192. entity.create_entity(entity_position, ["Mesh"])
  193. # 3) Access Entity Inspector
  194. editor_window = pyside_utils.get_editor_main_window()
  195. entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
  196. component_list_widget = entity_inspector.findChild(QtWidgets.QWidget, "m_componentListContents")
  197. # 4) Click on Asset Picker (Model Asset)
  198. general.select_object("TestEntity")
  199. general.idle_wait(0.5)
  200. model_asset = component_list_widget.findChildren(QtWidgets.QFrame, "Model Asset")[0]
  201. attached_button = model_asset.findChildren(QtWidgets.QPushButton, "attached-button")[0]
  202. # Assign Model Asset via OK button
  203. pyside_utils.click_button_async(attached_button)
  204. await asset_picker(["azmodel", "fbx"], "cedar (ModelAsset)", "ok")
  205. # 5) Verify if Model Asset is assigned
  206. try:
  207. mesh_success = await pyside_utils.wait_for_condition(lambda: is_asset_assigned(entity.components[0],
  208. "ok"))
  209. except pyside_utils.EventLoopTimeoutException as err:
  210. print(err)
  211. mesh_success = False
  212. model_asset_assigned_ok = (
  213. "Successfully assigned Model asset via OK button",
  214. "Failed to assign Model asset via OK button"
  215. )
  216. Report.result(model_asset_assigned_ok, mesh_success)
  217. # Clear Model Asset
  218. hydra.get_set_test(entity, 0, "Controller|Configuration|Model Asset", None)
  219. general.select_object("TestEntity")
  220. general.idle_wait(0.5)
  221. model_asset = component_list_widget.findChildren(QtWidgets.QFrame, "Model Asset")[0]
  222. attached_button = model_asset.findChildren(QtWidgets.QPushButton, "attached-button")[0]
  223. # Assign Model Asset via Enter
  224. pyside_utils.click_button_async(attached_button)
  225. await asset_picker(["azmodel", "fbx"], "cedar (ModelAsset)", "enter")
  226. # 5) Verify if Model Asset is assigned
  227. try:
  228. mesh_success = await pyside_utils.wait_for_condition(lambda: is_asset_assigned(entity.components[0],
  229. "enter"))
  230. except pyside_utils.EventLoopTimeoutException as err:
  231. print(err)
  232. mesh_success = False
  233. model_asset_assigned_enter = (
  234. "Successfully assigned Model Asset via Enter button",
  235. "Failed to assign Model Asset via Enter button"
  236. )
  237. Report.result(model_asset_assigned_enter, mesh_success)
  238. run_test()
  239. if __name__ == "__main__":
  240. from editor_python_test_tools.utils import Report
  241. Report.start_test(AssetPicker_UI_UX)