scripting_tools.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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. This file is being deprecated. relevant functions should be added to EditorPythonTestTools>script_canvas_tools_qt
  6. """
  7. from editor_python_test_tools.utils import TestHelper as helper
  8. from PySide2 import QtWidgets, QtTest, QtCore
  9. from PySide2.QtCore import Qt
  10. from editor_python_test_tools.utils import Report
  11. import pyside_utils
  12. import editor_python_test_tools.hydra_editor_utils as hydra
  13. from editor_python_test_tools.editor_entity_utils import EditorEntity
  14. import azlmbr.editor as editor
  15. import azlmbr.math as math
  16. import azlmbr.bus as bus
  17. import azlmbr.legacy.general as general
  18. import azlmbr.scriptcanvas as scriptcanvas
  19. from scripting_utils.scripting_constants import (SCRIPT_CANVAS_UI, ASSET_EDITOR_UI, NODE_PALETTE_UI, NODE_PALETTE_QT,
  20. TREE_VIEW_QT, SEARCH_FRAME_QT, SEARCH_FILTER_QT, SAVE_STRING, NAME_STRING,
  21. SAVE_ASSET_AS, WAIT_TIME_3, NODE_INSPECTOR_TITLE_KEY, WAIT_FRAMES, SCRIPT_EVENT_UI,
  22. EVENTS_QT, DEFAULT_SCRIPT_EVENT, PARAMETERS_QT, NODE_INSPECTOR_QT, NODE_INSPECTOR_UI,
  23. VARIABLE_PALETTE_QT, ADD_BUTTON_QT, VARIABLE_TYPES, ENTITY_STATES)
  24. class Tests():
  25. new_event_created = ("New Script Event created", "Failed to create a new event")
  26. child_event_created = ("Child Event created", "Failed to create Child Event")
  27. parameter_created = ("Successfully added parameter", "Failed to add parameter")
  28. parameter_removed = ("Successfully removed parameter", "Failed to remove parameter")
  29. def save_script_event_file(self, file_path):
  30. """
  31. function for saving a script event file with a user defined file path. Requires asset editor qt object to be initialized
  32. and any required fields in the asset editor to be filled in before asset can be saved.
  33. param self: the script object calling this function
  34. param file_path: full path to the file as a string
  35. returns: true if the Save action is successful and the * character disappears from the asset editor label
  36. """
  37. editor.AssetEditorWidgetRequestsBus(bus.Broadcast, SAVE_ASSET_AS, file_path)
  38. action = pyside_utils.find_child_by_pattern(self.asset_editor_menu_bar, {"type": QtWidgets.QAction, "iconText": SAVE_STRING})
  39. action.trigger()
  40. # wait till file is saved, to validate that check the text of QLabel at the bottom of the AssetEditor,
  41. # if there are no unsaved changes we will not have any * in the text
  42. label = self.asset_editor.findChild(QtWidgets.QLabel, "textEdit")
  43. return helper.wait_for_condition(lambda: "*" not in label.text(), WAIT_TIME_3)
  44. def initialize_editor_object(self):
  45. self.editor_main_window = pyside_utils.get_editor_main_window()
  46. def initialize_sc_editor_objects(self):
  47. self.sc_editor = self.editor_main_window.findChild(QtWidgets.QDockWidget, SCRIPT_CANVAS_UI)
  48. self.sc_editor_main_window = self.sc_editor.findChild(QtWidgets.QMainWindow)
  49. def initialize_asset_editor_object(self):
  50. """
  51. function for initializing qt objects needed for testing around asset editor
  52. param self: the script object calling this function.
  53. returns: None
  54. """
  55. self.asset_editor = self.editor_main_window.findChild(QtWidgets.QDockWidget, ASSET_EDITOR_UI)
  56. self.asset_editor_widget = self.asset_editor.findChild(QtWidgets.QWidget, "AssetEditorWindowClass")
  57. self.asset_editor_row_container = self.asset_editor_widget.findChild(QtWidgets.QWidget, "ContainerForRows")
  58. self.asset_editor_menu_bar = self.asset_editor_widget.findChild(QtWidgets.QMenuBar)
  59. def initialize_node_palette_object(self):
  60. """
  61. function for initializing qt objects needed for testing around the script canvas editor
  62. param self: the script object calling this function
  63. returns: None
  64. """
  65. self.node_palette = self.sc_editor.findChild(QtWidgets.QDockWidget, NODE_PALETTE_QT)
  66. self.node_tree_view = self.node_palette.findChild(QtWidgets.QTreeView, TREE_VIEW_QT)
  67. self.node_tree_search_frame = self.node_palette.findChild(QtWidgets.QFrame, SEARCH_FRAME_QT)
  68. self.node_tree_search_box = self.node_tree_search_frame.findChild(QtWidgets.QLineEdit, SEARCH_FILTER_QT)
  69. def expand_qt_container_rows(self, object_name):
  70. """
  71. function used for expanding qt container rows with expandable children
  72. param self: The script object calling this function
  73. param object_name: qt object name as a string
  74. returns: none
  75. """
  76. children = self.asset_editor_row_container.findChildren(QtWidgets.QFrame, object_name)
  77. for child in children:
  78. check_box = child.findChild(QtWidgets.QCheckBox)
  79. if check_box and not check_box.isChecked():
  80. QtTest.QTest.mouseClick(check_box, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
  81. def open_node_palette(self):
  82. """
  83. function for checking if node palette is on and if not turn it on
  84. param self: the script calling this function
  85. returns none
  86. """
  87. if self.sc_editor.findChild(QtWidgets.QDockWidget, NODE_PALETTE_QT) is None:
  88. action = pyside_utils.find_child_by_pattern(self.sc_editor, {"text": NODE_PALETTE_UI, "type": QtWidgets.QAction})
  89. action.trigger()
  90. def open_script_canvas():
  91. """
  92. function for opening the script canvas UI
  93. returns true / false result of helper function's attempt
  94. """
  95. general.open_pane(SCRIPT_CANVAS_UI)
  96. result = helper.wait_for_condition(lambda: general.is_pane_visible(SCRIPT_CANVAS_UI), WAIT_TIME_3)
  97. return result
  98. def open_asset_editor():
  99. """
  100. function for opening the asset editor UI
  101. returns true/false result of helper function's attempt
  102. """
  103. general.open_pane(ASSET_EDITOR_UI)
  104. result = helper.wait_for_condition(lambda: general.is_pane_visible(ASSET_EDITOR_UI), WAIT_TIME_3)
  105. return result
  106. def canvas_node_palette_search(self, node_name, number_of_retries):
  107. """
  108. function for searching the script canvas node palette for user defined nodes. function takes a number of retries as
  109. an argument in case editor/script canvas lags during test.
  110. param self: The script calling this function
  111. param node_name: the name of the node being searched for
  112. param number_of_retries: the number of times to search (click on the search button)
  113. returns: boolean value of the search attempt
  114. """
  115. self.node_tree_search_box.setText(node_name)
  116. helper.wait_for_condition(lambda: self.node_tree_search_box.text() == node_name, WAIT_TIME_3)
  117. # Try clicking ENTER in search box multiple times
  118. found_node = False
  119. for _ in range(number_of_retries):
  120. QtTest.QTest.keyClick(self.node_tree_search_box, QtCore.Qt.Key_Enter, QtCore.Qt.NoModifier)
  121. found_node = helper.wait_for_condition(
  122. lambda: pyside_utils.find_child_by_pattern(self.node_tree_view, {"text": node_name}) is not None, WAIT_TIME_3)
  123. if found_node is True:
  124. break
  125. return found_node
  126. def get_node_palette_node_tree_qt_object (self):
  127. """
  128. function for retrieving the tree view qt object for the node palette
  129. params self: the script calling this function
  130. returns: a tree view qt object
  131. """
  132. node_palette_widget = self.sc_editor.findChild(QtWidgets.QDockWidget, NODE_PALETTE_QT)
  133. node_palette_node_tree = node_palette_widget.findChild(QtWidgets.QTreeView, TREE_VIEW_QT)
  134. return node_palette_node_tree
  135. def get_node_palette_category_qt_object(self, category_name):
  136. """
  137. function for retrieving the qt object for a node palette category
  138. param self: the script calling this function
  139. param category_name: string for the category label you are searching node palette for
  140. returns: the qt object for the node palette category
  141. """
  142. node_palette_node_tree = get_node_palette_node_tree_qt_object(self)
  143. node_palette_category = pyside_utils.find_child_by_pattern(node_palette_node_tree, category_name)
  144. return node_palette_category
  145. def get_node_inspector_node_titles(self, sc_graph_node_inspector, sc_graph):
  146. """
  147. function for retrieving the node inspector's node titles from all nodes in a script canvas graph. function takes
  148. a script canvas graph and node inspector qt widget.
  149. param self: the script calling this function
  150. param sc_graph_node_inspector: the sc graph node inspector qt widget
  151. param sc_graph: the sc graph qt widget
  152. returns: a list of node titles (i.e Print - Utilities/Debug). If there are duplicates of a node then the title
  153. will include ( X Selected) in the string.
  154. """
  155. node_inspector_scroll_area = sc_graph_node_inspector.findChild(QtWidgets.QScrollArea, "")
  156. # perform ctrl+a keystroke to highlight all nodes on the graph
  157. QtTest.QTest.keyClick(sc_graph, "a", Qt.ControlModifier, WAIT_FRAMES)
  158. node_inspector_backgrounds = node_inspector_scroll_area.findChildren(QtWidgets.QFrame, "Background")
  159. titles = []
  160. for background in node_inspector_backgrounds:
  161. background_title = background.findChild(QtWidgets.QLabel, NODE_INSPECTOR_TITLE_KEY)
  162. if background_title.text() is not "":
  163. titles.append(background_title.text())
  164. return titles
  165. def create_new_sc_graph(sc_editor_main_window):
  166. """
  167. function for opening a new script canvas graph file. uses the sc editor window to trigger a new file action
  168. param self: the script calling this function
  169. param sc_editor_main_window: the qt object for the main sc_editor window
  170. returns: none
  171. """
  172. create_new_graph_action = pyside_utils.find_child_by_pattern(
  173. sc_editor_main_window, {"objectName": "action_New_Script", "type": QtWidgets.QAction}
  174. )
  175. create_new_graph_action.trigger()
  176. def create_new_variable(self, new_variable_type):
  177. """
  178. function for creating a new SC variable through variable manager
  179. param self: the script objecting calling this function
  180. param variable_type: The variable data type to create as a string. i.e "Boolean"
  181. returns: none
  182. """
  183. if type(new_variable_type) is not str:
  184. Report.critical_result(["Invalid variable type provided", ""], False)
  185. valid_type = False
  186. for this_type in VARIABLE_TYPES:
  187. if new_variable_type == this_type:
  188. valid_type = True
  189. if not valid_type:
  190. Report.critical_result(["Invalid variable type provided", ""], False)
  191. add_new_variable_button = self.variable_manager.findChild(QtWidgets.QPushButton, ADD_BUTTON_QT)
  192. add_new_variable_button.click() # Click on Create Variable button
  193. helper.wait_for_condition((
  194. lambda: self.variable_manager.findChild(QtWidgets.QTableView, VARIABLE_PALETTE_QT) is not None), WAIT_TIME_3)
  195. # Select variable type
  196. table_view = self.variable_manager.findChild(QtWidgets.QTableView, VARIABLE_PALETTE_QT)
  197. model_index = pyside_utils.find_child_by_pattern(table_view, new_variable_type)
  198. # Click on it to create variable
  199. pyside_utils.item_view_index_mouse_click(table_view, model_index)
  200. def get_sc_editor_node_inspector(sc_editor):
  201. """
  202. function for toggling the node inspector if it's not already turned on and returning the qt widget object
  203. param sc_editor: the script canvas editor qt object
  204. returns: the node inspector qt widget object
  205. """
  206. node_inspector_widget = sc_editor.findChild(QtWidgets.QDockWidget, NODE_INSPECTOR_QT)
  207. if sc_editor.findChild(QtWidgets.QDockWidget, NODE_INSPECTOR_QT) is None:
  208. action = pyside_utils.find_child_by_pattern(sc_editor, {"text": NODE_INSPECTOR_UI, "type": QtWidgets.QAction})
  209. action.trigger()
  210. return node_inspector_widget
  211. def create_script_event(self):
  212. """
  213. Function for creating a script event from the editor's asset editor.
  214. param self: the script calling this function
  215. returns None
  216. """
  217. action = pyside_utils.find_child_by_pattern(self.asset_editor_menu_bar, {"type": QtWidgets.QAction, "text": SCRIPT_EVENT_UI})
  218. action.trigger()
  219. result = helper.wait_for_condition(
  220. lambda: self.asset_editor_row_container.findChild(QtWidgets.QFrame, EVENTS_QT) is not None, WAIT_TIME_3
  221. )
  222. Report.result(Tests.new_event_created, result)
  223. # Add new child event
  224. add_event = self.asset_editor_row_container.findChild(QtWidgets.QFrame, EVENTS_QT).findChild(QtWidgets.QToolButton, "")
  225. add_event.click()
  226. result = helper.wait_for_condition(
  227. lambda: self.asset_editor_widget.findChild(QtWidgets.QFrame, DEFAULT_SCRIPT_EVENT) is not None, WAIT_TIME_3
  228. )
  229. Report.result(Tests.child_event_created, result)
  230. def create_script_event_parameter(self):
  231. add_param = self.asset_editor_row_container.findChild(QtWidgets.QFrame, "Parameters").findChild(QtWidgets.QToolButton, "")
  232. add_param.click()
  233. result = helper.wait_for_condition(
  234. lambda: self.asset_editor_widget.findChild(QtWidgets.QFrame, "[0]") is not None, WAIT_TIME_3
  235. )
  236. Report.result(Tests.parameter_created, result)
  237. def remove_script_event_parameter(self):
  238. remove_param = self.asset_editor_row_container.findChild(QtWidgets.QFrame, "[0]").findChild(QtWidgets.QToolButton, "")
  239. remove_param.click()
  240. result = helper.wait_for_condition(
  241. lambda: self.asset_editor_widget.findChild(QtWidgets.QFrame, "[0]") is None, WAIT_TIME_3
  242. )
  243. Report.result(Tests.parameter_removed, result)
  244. def add_empty_parameter_to_script_event(self, number_of_parameters):
  245. """
  246. Function for adding a new blank parameter to a script event
  247. param self: the script calling this function
  248. param number_of_parameters: the number of empty parameters to add
  249. returns none
  250. """
  251. helper.wait_for_condition(
  252. lambda: self.asset_editor_row_container.findChild(QtWidgets.QFrame, PARAMETERS_QT) is not None, WAIT_TIME_3)
  253. parameters = self.asset_editor_row_container.findChild(QtWidgets.QFrame, PARAMETERS_QT)
  254. add_parameter = parameters.findChild(QtWidgets.QToolButton, "")
  255. for _ in range(number_of_parameters):
  256. add_parameter.click()
  257. def get_script_event_parameter_name_text(self):
  258. """
  259. function for retrieving the name field of script event parameters
  260. param self: the script calling this function
  261. returns a container with all the parameters' editable name fields
  262. """
  263. parameter_names = self.asset_editor_row_container.findChildren(QtWidgets.QFrame, NAME_STRING)
  264. name_fields = []
  265. for parameter_name in parameter_names:
  266. name_fields.append(parameter_name.findChild(QtWidgets.QLineEdit))
  267. return name_fields
  268. def get_script_event_parameter_type_combobox(self):
  269. """
  270. function for retrieving the type field of script event parameters
  271. param self: the script calling this function
  272. returns a container with all the parameters' editable type combo boxes
  273. """
  274. parameter_types = self.asset_editor_row_container.findChildren(QtWidgets.QFrame, "Type")
  275. type_combo_boxes =[]
  276. for parameter_type in parameter_types:
  277. type_combo_boxes.append(parameter_type.findChild(QtWidgets.QComboBox))
  278. return type_combo_boxes
  279. def located_expected_tracer_lines(self, section_tracer, lines):
  280. """
  281. function for parsing game mode's console output for expected test lines. requires section_tracer. duplicates lines
  282. and error lines are not handled by this function
  283. param self: The script calling this function
  284. param section_tracer: python editor tracer object
  285. param lines: list of expected lines
  286. returns true if all the expected lines were detected in the parsed output
  287. """
  288. found_lines = [printInfo.message.strip() for printInfo in section_tracer.prints]
  289. expected_lines = len(lines)
  290. matching_lines = 0
  291. for line in lines:
  292. for found_line in found_lines:
  293. if line == found_line:
  294. matching_lines += 1
  295. return matching_lines >= expected_lines
  296. def located_expected_tracer_lines(section_tracer, lines):
  297. """
  298. function for parsing game mode's console output for expected test lines. requires section_tracer. duplicates lines
  299. and error lines are not handled by this function
  300. param self: The script calling this function
  301. param section_tracer: python editor tracer object
  302. param lines: list of expected lines
  303. returns true if all the expected lines were detected in the parsed output
  304. """
  305. found_lines = [printInfo.message.strip() for printInfo in section_tracer.prints]
  306. expected_lines = len(lines)
  307. matching_lines = 0
  308. for line in lines:
  309. for found_line in found_lines:
  310. if line == found_line:
  311. matching_lines += 1
  312. return matching_lines >= expected_lines
  313. def create_entity_with_sc_component_asset(entity_name, source_file, position = math.Vector3(512.0, 512.0, 32.0)):
  314. """
  315. function for creating a new entity in the scene w/ a script canvas component. Function also adds as
  316. script canvas file to the script canvas component's source file property.
  317. param entity_name: the name you want to assign the entity
  318. param source_file: the path to script canvas file to be added to the script canvas component
  319. param position: the translation property of the new entity's transform
  320. returns: the entity created by this function
  321. """
  322. sourcehandle = scriptcanvas.SourceHandleFromPath(source_file)
  323. entity = hydra.Entity(entity_name)
  324. entity.create_entity(position, ["Script Canvas"])
  325. script_canvas_component = entity.components[0]
  326. hydra.set_component_property_value(script_canvas_component, SCRIPT_CANVAS_COMPONENT_PROPERTY_PATH, sourcehandle)
  327. return entity
  328. def create_entity_with_multiple_sc_component_asset(entity_name, source_files, position = math.Vector3(512.0, 512.0, 32.0)):
  329. """
  330. function for creating a new entity with multiple script canvas components and adding a source file to each.
  331. param entity_name: the name you want to assign the entity
  332. param source_files: a list of source files you want added to the script canvas components
  333. param position: the translation property of the new entity's transform
  334. returns: the entity created by this function
  335. """
  336. number_of_files = len(source_files)
  337. components_array =[]
  338. for num in range(number_of_files):
  339. components_array.append("Script Canvas")
  340. entity = hydra.Entity(entity_name)
  341. entity.create_entity(position, components_array)
  342. for num in range(number_of_files):
  343. script_canvas_component = entity.components[num]
  344. sourcehandle = scriptcanvas.SourceHandleFromPath(source_files[num])
  345. hydra.set_component_property_value(script_canvas_component, SCRIPT_CANVAS_COMPONENT_PROPERTY_PATH, sourcehandle)
  346. return entity
  347. def change_entity_sc_variable_entity(entity_name, target_entity_name, component_property_path, component_index = 0):
  348. """
  349. function for finding an entity by its name and changing an exposed entity variable on the script canvas component.
  350. param entity_name: the string name of the entity you want to modify
  351. param target_entity_name: The name of the entity you want to use as a variable
  352. param component_property_path: The component property path to the variable.
  353. param
  354. parama component_index: the index of the sc component. default 0
  355. returns None
  356. """
  357. entity = EditorEntity.find_editor_entity(entity_name)
  358. target_entity = EditorEntity.find_editor_entity(target_entity_name)
  359. sc_component = entity.get_components_of_type(["Script Canvas"])[component_index]
  360. sc_component.set_component_property_value(component_property_path, target_entity.id)
  361. def change_entity_start_status(entity_name, start_status):
  362. """
  363. function for finding an entity by name and changing its start status
  364. param entity_name: the string name of the entity you want to modify
  365. param start_status: the start status to update (active/inactive)
  366. returns None
  367. """
  368. entity = EditorEntity.find_editor_entity(entity_name)
  369. entity.set_start_status(start_status)
  370. def validate_entity_start_state_by_name(entity_name, expected_state):
  371. """
  372. function for finding and validating the start state of an entity by name. If the actual state does not match
  373. the expected state the function will attempt to set the state for you.
  374. param entity_name: the name of the entity you want to modify
  375. param expected_state: the expected start state of the entity
  376. """
  377. entity = EditorEntity.find_editor_entity(entity_name)
  378. if expected_state.lower() not in ENTITY_STATES.keys():
  379. raise ValueError(f"{expected_state} is an invalid option; valid options: active, inactive, or editor.")
  380. state = entity.get_start_status()
  381. if state != ENTITY_STATES[expected_state]:
  382. # If state fails to set, set_start_status will assert
  383. entity.set_start_status(expected_state)
  384. state = entity.get_start_status()
  385. # return the start state that we were able to set the entity to
  386. return state == ENTITY_STATES[expected_state]
  387. def validate_entity_exists_by_name(entity_name, test_results):
  388. """
  389. function for validating if an entity was properly created
  390. param entity_name: string name of the entity to validate
  391. param test_results: pass/fail tuple of result strings
  392. """
  393. entity = EditorEntity.find_editor_entity(entity_name)
  394. Report.critical_result(test_results, entity.id.IsValid())
  395. return entity