TestStepMaster.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. import baangt.base.GlobalConstants as GC
  2. from baangt.base.Timing.Timing import Timing
  3. from baangt.base.BrowserHandling.BrowserHandling import BrowserDriver
  4. from baangt.base.ApiHandling import ApiHandling
  5. import sys
  6. from pkg_resources import parse_version
  7. import logging
  8. from baangt.TestSteps.Exceptions import baangtTestStepException
  9. from baangt.TestSteps.AddressCreation import AddressCreate
  10. logger = logging.getLogger("pyC")
  11. class TestStepMaster:
  12. def __init__(self, **kwargs):
  13. self.kwargs = kwargs
  14. self.testRunInstance = kwargs.get(GC.KWARGS_TESTRUNINSTANCE)
  15. self.testcaseDataDict = kwargs.get(GC.KWARGS_DATA)
  16. self.timing: Timing = kwargs.get(GC.KWARGS_TIMING)
  17. self.timingName = self.timing.takeTime(self.__class__.__name__, forceNew=True)
  18. self.browserSession: BrowserDriver = kwargs.get(GC.KWARGS_BROWSER)
  19. self.apiSession: ApiHandling = kwargs.get(GC.KWARGS_API_SESSION)
  20. self.testCaseStatus = None
  21. self.testStepNumber = kwargs.get(GC.STRUCTURE_TESTSTEP) # Set in TestRun by TestCaseMaster
  22. self.testRunUtil = self.testRunInstance.testRunUtils
  23. # check, if this TestStep has additional Parameters and if so, execute
  24. lSequence = self.testRunUtil.getSequenceByNumber(testRunName=self.testRunInstance.testRunName,
  25. sequence=kwargs.get(GC.STRUCTURE_TESTCASESEQUENCE))
  26. lTestCase = self.testRunUtil.getTestCaseByNumber(lSequence, kwargs.get(GC.STRUCTURE_TESTCASE))
  27. lTestStep = self.testRunUtil.getTestStepByNumber(lTestCase, kwargs.get(GC.STRUCTURE_TESTSTEP))
  28. self.globalRelease = self.testRunInstance.globalSettings.get("Release", "")
  29. self.ifActive = False
  30. self.ifIsTrue = True
  31. if not isinstance(lTestStep, str):
  32. # This TestStepMaster-Instance should actually do something - activitites are described
  33. # in the TestExecutionSteps
  34. self.executeDirect(lTestStep[1][GC.STRUCTURE_TESTSTEPEXECUTION])
  35. self.teardown()
  36. def executeDirect(self, executionCommands):
  37. """
  38. This will execute single Operations directly
  39. """
  40. for key, command in executionCommands.items():
  41. # when we have an IF-condition and it's condition was not TRUE, then skip whatever comes here until we
  42. # reach Endif
  43. if not self.ifIsTrue and command["Activity"] != "ENDIF":
  44. continue
  45. lActivity = command["Activity"].upper()
  46. if lActivity == "COMMENT":
  47. continue # Comment's are ignored
  48. lLocatorType = command["LocatorType"].upper()
  49. lLocator = self.replaceVariables(command["Locator"])
  50. if lLocator and not lLocatorType: # If locatorType is empty, default it to XPATH
  51. lLocatorType = 'XPATH'
  52. xpath, css, id = self.__setLocator(lLocatorType, lLocator)
  53. lValue = str(command["Value"])
  54. lValue2 = str(command["Value2"])
  55. lComparison = command["Comparison"]
  56. lOptional = TestStepMaster._sanitizeXField(command["Optional"])
  57. # check release line
  58. lRelease = command["Release"]
  59. lTimeout = TestStepMaster.__setTimeout(command["Timeout"])
  60. # Replace variables from data file
  61. if len(lValue) > 0:
  62. lValue = self.replaceVariables(lValue)
  63. if len(lValue2) > 0:
  64. lValue2 = self.replaceVariables(lValue2)
  65. if not TestStepMaster.ifQualifyForExecution(self.globalRelease, lRelease):
  66. logger.debug(f"we skipped this line due to {lRelease} disqualifies according to {self.globalRelease} ")
  67. continue # We ignored the steps as it doesn't qualify
  68. if lActivity == "GOTOURL":
  69. self.browserSession.goToUrl(lValue)
  70. elif lActivity == "SETTEXT":
  71. self.browserSession.findByAndSetText(xpath=xpath, css=css, id=id, value=lValue, timeout=lTimeout)
  72. elif lActivity == 'HANDLEIFRAME':
  73. self.browserSession.handleIframe(lLocator)
  74. elif lActivity == "CLICK":
  75. self.browserSession.findByAndClick(xpath=xpath, css=css, id=id, timeout=lTimeout)
  76. elif lActivity == "PAUSE":
  77. self.browserSession.sleep(sleepTimeinSeconds=lValue)
  78. elif lActivity == "IF":
  79. if self.ifActive:
  80. raise BaseException("No nested IFs at this point, sorry...")
  81. self.ifActive = True
  82. # Originally we had only Comparisons. Now we also want to check for existance of Field
  83. if not lValue and lLocatorType and lLocator:
  84. lValue = self.browserSession.findBy(xpath=xpath, css=css, id=id, optional=lOptional,
  85. timeout=lTimeout)
  86. self.__doComparisons(lComparison=lComparison, value1=lValue, value2=lValue2)
  87. elif lActivity == "ENDIF":
  88. if not self.ifActive:
  89. raise BaseException("ENDIF without IF")
  90. self.ifActive = False
  91. self.ifIsTrue = True
  92. elif lActivity == 'GOBACK':
  93. self.browserSession.goBack()
  94. elif lActivity == 'APIURL':
  95. self.apiSession.setBaseURL(lValue)
  96. elif lActivity == 'ENDPOINT':
  97. self.apiSession.setEndPoint(lValue)
  98. elif lActivity == 'POST':
  99. self.apiSession.postURL(content=lValue)
  100. elif lActivity == 'GET':
  101. self.apiSession.getURL()
  102. elif lActivity == 'HEADER':
  103. self.apiSession.setHeaders(setHeaderData=lValue)
  104. elif lActivity == 'SAVE':
  105. self.doSaveData(lValue, lValue2)
  106. elif lActivity == "ADDRESS_CREATE":
  107. # Create Address with option lValue and lValue2
  108. AddressCreate(lValue,lValue2)
  109. # Update testcaseDataDict with addressDict returned from
  110. AddressCreate.returnAddress()
  111. self.testcaseDataDict.update(AddressCreate.returnAddress())
  112. elif lActivity == 'ASSERT':
  113. value_found = self.browserSession.findByAndWaitForValue(xpath=xpath, css=css, id=id, optional=lOptional,
  114. timeout=lTimeout)
  115. value_expected = lValue
  116. if value_expected != value_found:
  117. raise baangtTestStepException(f"Expected Value: {value_expected}, Value found :{value_found} ")
  118. elif lActivity == 'IBAN':
  119. # Create Random IBAN. Value1 = Input-Parameter for IBAN-Function. Value2=Fieldname
  120. self.__getIBAN(lValue, lValue2)
  121. else:
  122. raise BaseException(f"Unknown command in TestStep {lActivity}")
  123. def __getIBAN(self, lValue, lValue2):
  124. from baangt.base.IBAN import IBAN
  125. if not lValue2:
  126. logger.critical("IBAN-Method called without destination field name in column 'Value 2'")
  127. return
  128. lIBAN = IBAN()
  129. self.testcaseDataDict[lValue2] = lIBAN.getRandomIBAN()
  130. pass
  131. @staticmethod
  132. def _sanitizeXField(inField):
  133. """
  134. When "X" or "True" is sent, then use this
  135. @param inField:
  136. @return:
  137. """
  138. lXField = True
  139. if not inField:
  140. lXField = False
  141. if inField.upper() == 'FALSE':
  142. lXField = False
  143. return lXField
  144. @staticmethod
  145. def ifQualifyForExecution(version_global, version_line):
  146. """ This function will test version_global and version_line
  147. @return True or False
  148. """
  149. if not version_global.strip():
  150. # No value is defined in Release, return True
  151. return True
  152. if not version_line.strip():
  153. # we skipped this line
  154. return True
  155. # split the version line
  156. if not len(version_line.strip().split(" ")) == 2:
  157. logger.debug(f"Invalid release format {version_line} ")
  158. return True
  159. comp_operator, version = version_line.strip().split(" ")
  160. if comp_operator == "<":
  161. return parse_version(version_global) < parse_version(version)
  162. elif comp_operator == ">":
  163. return parse_version(version_global) > parse_version(version)
  164. elif comp_operator == ">=":
  165. return parse_version(version_global) >= parse_version(version)
  166. elif comp_operator == "<=":
  167. return parse_version(version_global) <= parse_version(version)
  168. elif comp_operator == "=" or comp_operator == "==":
  169. return parse_version(version_global) == parse_version(version)
  170. else:
  171. logger.debug(f"Global version {version_global}, line version {version_line} ")
  172. return False
  173. def doSaveData(self, toField, valueForField):
  174. self.testcaseDataDict[toField] = valueForField
  175. @staticmethod
  176. def __setLocator(lLocatorType, lLocator):
  177. """
  178. @param lLocatorType: XPATH, CSS, ID, etc.
  179. @param lLocator: Value of the locator
  180. @return:
  181. """
  182. xpath = None
  183. css = None
  184. lId = None
  185. if lLocatorType:
  186. if lLocatorType == 'XPATH':
  187. xpath = lLocator
  188. elif lLocatorType == 'CSS':
  189. css = lLocator
  190. elif lLocatorType == 'ID':
  191. lId = lLocator
  192. return xpath, css, lId
  193. @staticmethod
  194. def __setTimeout(lTimeout):
  195. if lTimeout:
  196. lTimeout = float(lTimeout)
  197. else:
  198. lTimeout = 20
  199. return lTimeout
  200. @staticmethod
  201. def anyting2Boolean(valueIn):
  202. if isinstance(valueIn, bool):
  203. return valueIn
  204. if isinstance(valueIn, int):
  205. return bool(valueIn)
  206. if isinstance(valueIn, str):
  207. if valueIn.lower() in ("yes", "true", "1", "ok"):
  208. return True
  209. else:
  210. return False
  211. raise TypeError(f"Anything2Boolean had a wrong value: {valueIn}. Don't know how to convert that to boolean")
  212. def __doComparisons(self, lComparison, value1, value2):
  213. if isinstance(value1, bool) or isinstance(value2, bool):
  214. value1 = TestStepMaster.anyting2Boolean(value1)
  215. value2 = TestStepMaster.anyting2Boolean(value2)
  216. if lComparison == "=":
  217. if value1 == value2:
  218. self.ifIsTrue = True
  219. else:
  220. self.ifIsTrue = False
  221. elif lComparison == ">":
  222. if value1 > value2:
  223. self.ifIsTrue = True
  224. else:
  225. self.ifIsTrue = False
  226. elif lComparison == "<":
  227. if value1 < value2:
  228. self.ifIsTrue = True
  229. else:
  230. self.ifIsTrue = False
  231. else:
  232. raise BaseException(f"Comparison Operator not supported/unknown {lComparison}")
  233. def execute(self):
  234. """Method is overwritten in all children/subclasses"""
  235. pass
  236. def teardown(self):
  237. if self.testCaseStatus:
  238. if not self.testcaseDataDict[GC.TESTCASESTATUS]:
  239. self.testcaseDataDict[GC.TESTCASESTATUS] = self.testCaseStatus
  240. elif self.testCaseStatus == GC.TESTCASESTATUS_SUCCESS:
  241. # We'll not overwrite a potential Error Status of the testcase
  242. pass
  243. elif self.testCaseStatus == GC.TESTCASESTATUS_ERROR:
  244. self.testcaseDataDict = GC.TESTCASESTATUS_ERROR
  245. else:
  246. sys.exit("No idea, what happened here. Unknown condition appeared")
  247. self.timing.takeTime(self.timingName)
  248. def replaceVariables(self, expression):
  249. """
  250. The syntax for variables is currently $(<column_name_from_data_file>). Multiple variables can be assigned
  251. in one cell, for instance perfectly fine: "http://$(BASEURL)/$(ENDPOINT)"
  252. @param expression: the current cell, either as fixed value, e.g. "Franzi" or with a varible $(DATE)
  253. @return: the replaced value, e.g. if expression was $(DATE) and the value in column "DATE" of data-file was
  254. "01.01.2020" then return will be "01.01.2020"
  255. """
  256. if "$(" not in expression:
  257. return expression
  258. while "$(" in expression:
  259. if expression[0:2] == "$(":
  260. left_part = ""
  261. else:
  262. left_part = expression.split("$(")[0]
  263. center = expression[len(left_part)+2:]
  264. center = center.split(")")[0]
  265. right_part = expression[len(left_part)+len(center)+3:]
  266. if "." not in center:
  267. # Replace the variable with the value from data structure
  268. center = self.testcaseDataDict.get(center)
  269. else:
  270. # This is a reference to a DICT with ".": for instance APIHandling.AnswerContent("<bla>")
  271. dictVariable = center.split(".")[0]
  272. dictValue = center.split(".")[1]
  273. if dictVariable == 'ANSWER_CONTENT':
  274. center = self.apiSession.session[1].answerJSON.get(dictValue, "Empty")
  275. else:
  276. raise BaseException(f"Missing code to replace value for: {center}")
  277. if not center:
  278. raise BaseException(f"Variable not found: {center}")
  279. expression = "".join([left_part, center, right_part])
  280. return expression