TestStepMaster.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  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. from baangt.base.PDFCompare import PDFCompare, PDFCompareDetails
  11. from baangt.base.Faker import Faker as baangtFaker
  12. from baangt.base.Utils import utils
  13. from baangt.base.RuntimeStatistics import Statistic
  14. import random
  15. import itertools
  16. import json
  17. from baangt.TestSteps.RandomValues import RandomValues
  18. logger = logging.getLogger("pyC")
  19. class TestStepMaster:
  20. def __init__(self, executeDirect=True, **kwargs):
  21. self.anchor = None
  22. self.anchorLocator = None
  23. self.anchorLocatorType = None
  24. self.testCaseStatus = None
  25. self.ifIsTrue = True # used to know if command is inside if condition and run command as per it
  26. self.elseIsTrue = False # used to know if command is inside else condition
  27. self.ifLis = [self.ifIsTrue] # useful in storing state of nested if conditions
  28. self.elseLis = [self.elseIsTrue] # useful in storing state of nested else conditions
  29. self.ifConditions = 0 # use to verify endif
  30. self.repeatIsTrue = False # use to know if commands are running inside repeat loop
  31. self.repeatCommands = [] # used to store steps command of repeat data
  32. self.repeatReplaceDataDictionary = [] # used to store RLP_ data to be looped in repeat
  33. self.repeatCount = [] # Used to store count of randomdata in loop
  34. self.repeatActive = 0 # to sync active repeat counts with repeat done and will be execute when both are equal
  35. self.repeatDone = 0
  36. self.baangtFaker = None
  37. self.statistics = Statistic()
  38. self.kwargs = kwargs
  39. self.testRunInstance = kwargs.get(GC.KWARGS_TESTRUNINSTANCE)
  40. self.testcaseDataDict = kwargs.get(GC.KWARGS_DATA)
  41. self.timing: Timing = kwargs.get(GC.KWARGS_TIMING)
  42. self.timingName = self.timing.takeTime(self.__class__.__name__, forceNew=True)
  43. self.browserSession: BrowserDriver = kwargs.get(GC.KWARGS_BROWSER)
  44. self.apiSession: ApiHandling = kwargs.get(GC.KWARGS_API_SESSION)
  45. self.testStepNumber = kwargs.get(GC.STRUCTURE_TESTSTEP) # Set in TestRunData by TestCaseMaster
  46. self.testRunUtil = self.testRunInstance.testRunUtils
  47. # Get the release from testrun-customizing if set: (will influence, whether or not elements will be executed)
  48. self.globalRelease = self.testRunInstance.globalSettings.get("Release", "")
  49. # check, if this TestStep has additional Parameters and if so, execute
  50. lSequence = self.testRunUtil.getSequenceByNumber(testRunName=self.testRunInstance.testRunName,
  51. sequence=kwargs.get(GC.STRUCTURE_TESTCASESEQUENCE))
  52. self.testCase = self.testRunUtil.getTestCaseByNumber(lSequence, kwargs.get(GC.STRUCTURE_TESTCASE))
  53. self.testStep = self.testRunUtil.getTestStepByNumber(self.testCase, self.testStepNumber)
  54. self.randomValues = RandomValues()
  55. try:
  56. if self.testStep and len(self.testStep) > 1:
  57. if not isinstance(self.testStep[1], str) and executeDirect:
  58. # This TestStepMaster-Instance should actually do something - activitites are described
  59. # in the TestExecutionSteps.
  60. # Otherwise there's only a classname in TestStep[0]
  61. self.executeDirect(self.testStep[1][GC.STRUCTURE_TESTSTEPEXECUTION])
  62. # Teardown makes only sense, when we actually executed something directly in here
  63. # Otherwise (if it was 1 or 2 Tab-stops more to the left) we'd take execution time without
  64. # having done anything
  65. self.teardown()
  66. except Exception as e:
  67. logger.warning(f"Uncought exception {e}")
  68. utils.traceback(exception_in=e)
  69. self.statistics.update_teststep_sequence()
  70. def executeDirect(self, executionCommands):
  71. """
  72. Executes a sequence of Commands. Will be subclassed in other modules.
  73. :param executionCommands:
  74. :return:
  75. """
  76. for index, (key, command) in enumerate(executionCommands.items()):
  77. try:
  78. self.executeDirectSingle(index, command)
  79. except Exception as ex:
  80. # 2020-07-16: This Exception is cought, printed and then nothing. That's not good. The test run
  81. # continues forever, despite this exception. Correct way would be to set test case to error and stop
  82. # this test case
  83. # Before we change something here we should check, why the calling function raises an error.
  84. logger.critical(ex)
  85. self.testcaseDataDict[GC.TESTCASESTATUS] = GC.TESTCASESTATUS_ERROR
  86. return
  87. def manageNestedCondition(self, condition="", ifis=False):
  88. """
  89. Manages if and else condition. Specially made to deal with nested conditions
  90. :param condition:
  91. :param ifis:
  92. :return:
  93. """
  94. if condition.upper() == "IF" or condition.upper() == "IF_":
  95. self.ifConditions += 1
  96. self.ifLis.append(ifis)
  97. self.elseLis.append(False)
  98. elif condition.upper() == "ELSE" or condition.upper() == "ELSE_":
  99. self.elseLis[-1] = True
  100. elif condition.upper() == "ENDIF":
  101. self.ifConditions -= 1
  102. if self.ifConditions < 0:
  103. raise BaseException("Numbers of ENDIF are greater than IF.")
  104. self.ifLis.pop()
  105. self.elseLis.pop()
  106. assert len(self.ifLis) == self.ifConditions + 1 and len(self.elseLis) == self.ifConditions + 1
  107. self.ifIsTrue = self.ifLis[-1]
  108. self.elseIsTrue = self.elseLis[-1]
  109. def manageNestedLoops(self, command, commandNumber):
  110. """
  111. This method is used to deal with Repeat statements and nested repeat statements.
  112. It is called on when a repeat statement comes then this method will take and store all the commands come after
  113. it until it reaches repeat-done statement of same level i.e. when a repeat statement is present inside this
  114. repeat statement then the first repeat-done came will be considered as repeat-done of the nested repeat and the
  115. next one will be considered as the main.
  116. :param command:
  117. :param commandNumber:
  118. :return:
  119. """
  120. if command["Activity"].upper() == "REPEAT": # To sync active repeat with repeat done
  121. self.repeatCommands[-1][commandNumber] = command # storing nested repeat command
  122. self.repeatActive += 1 # used to know the level of repeat and repeat done
  123. return
  124. if command["Activity"].upper() != "REPEAT-DONE": # store command in repeatDict
  125. self.repeatCommands[-1][commandNumber] = command
  126. return
  127. else:
  128. self.repeatDone += 1 # to sync repeat done with active repeat
  129. if self.repeatDone < self.repeatActive: # if all repeat-done are not synced with repeat, store the data
  130. self.repeatCommands[-1][commandNumber] = command
  131. logger.info(command)
  132. return
  133. self.repeatIsTrue = False
  134. if self.repeatReplaceDataDictionary[-1]:
  135. data_list = []
  136. if type(self.repeatReplaceDataDictionary[-1]) is list:
  137. for data_dic in self.repeatReplaceDataDictionary[-1]:
  138. keys, values = zip(*data_dic.items())
  139. final_values = []
  140. for value in values: # coverting none list values to list. Useful in make all possible data using itertools
  141. if type(value) is not list:
  142. final_values.append([value])
  143. else:
  144. final_values.append(value)
  145. data_l = [dict(zip(keys, v)) for v in
  146. itertools.product(*final_values)] # itertools to make all possible combinations
  147. data_list.extend(data_l)
  148. else:
  149. data_list = [self.repeatReplaceDataDictionary[-1]]
  150. if len(self.repeatCount) > 0 and self.repeatCount[-1]: # get random data from list of data
  151. try:
  152. data_list = random.sample(data_list, int(self.repeatCount[-1]))
  153. except:
  154. pass
  155. for data in data_list:
  156. temp_dic = dict(self.repeatCommands[-1])
  157. processed_data = {}
  158. for key in temp_dic:
  159. try:
  160. processed_data = dict(temp_dic[key])
  161. except Exception as ex:
  162. logger.debug(ex)
  163. try:
  164. self.executeDirectSingle(key, processed_data, replaceFromDict=data)
  165. except Exception as ex:
  166. logger.info(ex)
  167. del self.repeatCommands[-1]
  168. del self.repeatReplaceDataDictionary[-1]
  169. del self.repeatCount[-1]
  170. self.repeatDone = 0
  171. self.repeatActive = 0
  172. return
  173. def executeDirectSingle(self, commandNumber, command, replaceFromDict=None):
  174. """
  175. This will execute a single instruction
  176. """
  177. # when we have an IF-condition and it's condition was not TRUE, then skip whatever comes here until we
  178. # reach Endif
  179. if not self.ifIsTrue and not self.elseIsTrue:
  180. if command["Activity"].upper() == "IF":
  181. self.manageNestedCondition(condition=command["Activity"].upper(), ifis=self.ifIsTrue)
  182. return True
  183. if command["Activity"].upper() != "ELSE" and command["Activity"].upper() != "ENDIF":
  184. return True
  185. if self.repeatIsTrue: # If repeat statement is active then execute this
  186. self.manageNestedLoops(command, commandNumber)
  187. return
  188. css, id, lActivity, lLocator, lLocatorType, xpath = self._extractAllSingleValues(command)
  189. if lActivity == "COMMENT":
  190. return # Comment's are ignored
  191. if self.anchor and xpath:
  192. if xpath[0:3] == '///': # Xpath doesn't want to use Anchor
  193. xpath = xpath[1:]
  194. logger.debug(f"Anchor active, but escaped. Using pure xpath: {xpath}")
  195. else:
  196. logger.debug(f"Anchor active. combining {self.anchorLocator} with {xpath}")
  197. xpath = self.anchorLocator + xpath
  198. lValue = str(command["Value"])
  199. lValue2 = str(command["Value2"])
  200. lComparison = command["Comparison"]
  201. lOptional = utils.anything2Boolean(command["Optional"])
  202. # check release line
  203. lRelease = command["Release"]
  204. # Timeout defaults to 20 seconds, if not set otherwise.
  205. lTimeout = TestStepMaster._setTimeout(command["Timeout"])
  206. lTimingString = f"TS {commandNumber} {lActivity.lower()}"
  207. self.timing.takeTime(lTimingString)
  208. logger.info(
  209. f"Executing TestStepDetail {commandNumber} with parameters: act={lActivity}, lType={lLocatorType}, loc={lLocator}, "
  210. f"Val1={lValue}, comp={lComparison}, Val2={lValue2}, Optional={lOptional}, timeout={lTimeout}")
  211. original_value = lValue # used as key to make rlp json dict and will be used further to make sheet name
  212. lValue, lValue2 = self.replaceAllVariables(lValue, lValue2, replaceFromDict=replaceFromDict)
  213. if not TestStepMaster.ifQualifyForExecution(self.globalRelease, lRelease):
  214. logger.debug(f"we skipped this line due to {lRelease} disqualifies according to {self.globalRelease} ")
  215. return
  216. if lActivity[0:3] == "ZZ_":
  217. # custom command. Do nothing and return
  218. return
  219. elif lActivity.lower() == "if": # Activities with matching name as python keywords has "_" in the end of their
  220. lActivity = "if_" # function name to avoid conflict, so changing if in activity too
  221. elif lActivity.lower() == "else":
  222. lActivity = "else_"
  223. elif lActivity.lower() == "assert":
  224. lActivity = "assert_"
  225. try:
  226. kwargs = locals()
  227. del kwargs["self"]
  228. getattr(self, lActivity.lower())(**kwargs)
  229. except AttributeError:
  230. raise BaseException(f"Unknown command in TestStep {lActivity}")
  231. self.timing.takeTime(lTimingString)
  232. def gotourl(self, **kwargs):
  233. if kwargs["lValue"]:
  234. self.browserSession.goToUrl(kwargs["lValue"])
  235. elif kwargs["lLocator"]:
  236. self.browserSession.goToUrl(kwargs["lLocator"])
  237. else:
  238. logger.critical("GotoURL without URL called. Aborting. "
  239. "Please provide URL either in Value or Locator columns")
  240. def waitforelementpresent(self, **kwargs):
  241. self.browserSession.findBy(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"],
  242. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  243. def settext(self, **kwargs):
  244. self.browserSession.findByAndSetText(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  245. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  246. def settextif(self, **kwargs):
  247. self.browserSession.findByAndSetTextIf(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  248. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  249. def forcetext(self, **kwargs):
  250. self.browserSession.findByAndForceText(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  251. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  252. def forcetextif(self, **kwargs):
  253. if kwargs["lValue"]:
  254. self.browserSession.findByAndForceText(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  255. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  256. def forcetextjs(self, **kwargs):
  257. if kwargs["lValue"]:
  258. self.browserSession.findByAndForceViaJS(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  259. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  260. def setanchor(self, **kwargs):
  261. if not kwargs["lLocator"]:
  262. self.anchor = None
  263. self.anchorLocator = None
  264. self.anchorLocatorType = None
  265. else:
  266. found = self.browserSession.findBy(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], timeout=kwargs["lTimeout"])
  267. if found:
  268. self.anchor = self.browserSession.element
  269. self.anchorLocator = kwargs["lLocator"]
  270. self.anchorLocatorType = kwargs["lLocatorType"]
  271. logger.debug(f'Anchor set: {kwargs["lLocatorType"]} {kwargs["lLocator"]}')
  272. else:
  273. logger.error(f'Anchor should be set, but can\'t be found in the current page: {kwargs["lLocatorType"]}, {kwargs["lLocator"]}')
  274. raise ValueError(f'Anchor should be set, but can\'t be found in the current page: {kwargs["lLocatorType"]}, {kwargs["lLocator"]}')
  275. def handleiframe(self, **kwargs):
  276. self.browserSession.handleIframe(kwargs["lLocator"])
  277. def switchwindow(self, **kwargs):
  278. lWindow = kwargs["lValue"]
  279. if lWindow.isnumeric():
  280. lWindow = int(lWindow)
  281. self.browserSession.handleWindow(windowNumber=lWindow, timeout=kwargs["lTimeout"])
  282. def click(self, **kwargs):
  283. self.browserSession.findByAndClick(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"],
  284. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  285. def clickif(self, **kwargs):
  286. self.browserSession.findByAndClickIf(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"],
  287. timeout=kwargs["lTimeout"], value=kwargs["lValue"], optional=kwargs["lOptional"])
  288. def pause(self, **kwargs):
  289. self.browserSession.sleep(seconds=float(kwargs["lValue"]))
  290. def if_(self, **kwargs):
  291. # Originally we had only Comparisons. Now we also want to check for existance of Field
  292. if not kwargs["lValue"] and kwargs["lLocatorType"] and kwargs["lLocator"]:
  293. kwargs["lValue"] = self.browserSession.findBy(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], optional=kwargs["lOptional"],
  294. timeout=kwargs["lTimeout"])
  295. self.__doComparisons(kwargs["lComparison"], value1=kwargs["lValue"], value2=kwargs["lValue"])
  296. logger.debug(f'IF-condition original Value: {kwargs["original_value"]} (transformed: {kwargs["lValue"]}) '
  297. f'{kwargs["lComparison"]} {kwargs["lValue"]} evaluated to: {self.ifIsTrue} ')
  298. def else_(self, **kwargs):
  299. if not self.ifIsTrue: # don't run else statement if "if" statement is true
  300. self.manageNestedCondition(condition=kwargs["lActivity"])
  301. logger.debug("Executing ELSE-condition")
  302. else:
  303. self.ifIsTrue = False
  304. def endif(self, **kwargs):
  305. self.manageNestedCondition(condition=kwargs["lActivity"])
  306. def repeat(self, **kwargs):
  307. self.repeatActive += 1
  308. self.repeatIsTrue = True
  309. self.repeatCommands.append({})
  310. if kwargs["original_value"] not in self.testRunInstance.json_dict:
  311. self.testRunInstance.json_dict[kwargs["original_value"]] = []
  312. self.testRunInstance.json_dict[kwargs["original_value"]].append(kwargs["lValue"])
  313. self.repeatReplaceDataDictionary.append(kwargs["lValue"])
  314. self.repeatCount.append(kwargs["lValue"])
  315. def goback(self, **kwargs):
  316. self.browserSession.goBack()
  317. def apiurl(self, **kwargs):
  318. self.apiSession.setBaseURL(kwargs["lValue"])
  319. def endpoint(self, **kwargs):
  320. self.apiSession.setEndPoint(kwargs["lValue"])
  321. def post(self, **kwargs):
  322. self.apiSession.postURL(content=kwargs["lValue"])
  323. def get(self, **kwargs):
  324. self.apiSession.getURL()
  325. def header(self, **kwargs):
  326. self.apiSession.setHeaders(setHeaderData=kwargs["lValue"])
  327. def save(self, **kwargs):
  328. self.doSaveData(kwargs["lValue"], kwargs["lValue"], kwargs["lLocatorType"], kwargs["lLocator"])
  329. def clear(self, **kwargs):
  330. # Clear a variable:
  331. if self.testcaseDataDict.get(kwargs["lValue"]):
  332. del self.testcaseDataDict[kwargs["lValue"]]
  333. def saveto(self, **kwargs):
  334. # In this case, we need to parse the real field, not the representation of the replaced field value
  335. self.doSaveData(kwargs["command"]['Value'], kwargs["lValue"], kwargs["lLocatorType"], kwargs["lLocator"])
  336. def submit(self, **kwargs):
  337. self.browserSession.submit()
  338. def address_create(self, **kwargs):
  339. # Create Address with option kwargs["lValue"] and kwargs["lValue"]
  340. AddressCreate(kwargs["lValue"], kwargs["lValue"])
  341. # Update testcaseDataDict with addressDict returned from
  342. AddressCreate.returnAddress()
  343. self.testcaseDataDict.update(AddressCreate.returnAddress())
  344. def assert_(self, **kwargs):
  345. value_found = self.browserSession.findByAndWaitForValue(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"],
  346. optional=kwargs["lOptional"], timeout=kwargs["lTimeout"])
  347. if not self.__doComparisons(kwargs["lComparison"], value1=value_found, value2=kwargs["lValue"]):
  348. logger.error(f'Condition {value_found} {kwargs["lComparison"]} {kwargs["lValue"]} was not met during assert.')
  349. raise baangtTestStepException(f'Expected Value: {kwargs["lValue"]}, Value found :{value_found} ')
  350. def iban(self, **kwargs):
  351. # Create Random IBAN. Value1 = Input-Parameter for IBAN-Function. Value2=Fieldname
  352. self.__getIBAN(kwargs["lValue"], kwargs["lValue2"])
  353. def pdfcompare(self, **kwargs):
  354. self.doPDFComparison(kwargs["lValue"])
  355. def checklinks(self, **kwargs):
  356. self.checkLinks()
  357. def alertif(self, **kwargs):
  358. self.browserSession.confirmAlertIfAny()
  359. def tcstoptestcase(self, **kwargs):
  360. self.testcaseDataDict[GC.TESTCASESTATUS_STOP] = "X" # will stop the test case
  361. def tcstoptestcaseerror(self, **kwargs):
  362. self.testcaseDataDict[GC.TESTCASESTATUS_STOPERROR] = "X" # will stop the test case and set error
  363. def _extractAllSingleValues(self, command):
  364. lActivity = command["Activity"].upper()
  365. lLocatorType = command["LocatorType"].upper()
  366. try:
  367. lLocator = self.replaceVariables(command["Locator"])
  368. except Exception as ex:
  369. logger.info(ex)
  370. if lLocator and not lLocatorType: # If locatorType is empty, default it to XPATH
  371. lLocatorType = 'XPATH'
  372. xpath, css, id = self.__setLocator(lLocatorType, lLocator)
  373. return css, id, lActivity, lLocator, lLocatorType, xpath
  374. def doPDFComparison(self, lValue, lFieldnameForResults="DOC_Compare"):
  375. lFiles = self.browserSession.findNewFiles()
  376. if len(lFiles) > 1:
  377. # fixme: Do something! There were more than 1 files since last check. Damn
  378. logger.critical(f"There were {len(lFiles)} files new since last check. Can't handle that. ")
  379. raise Exception
  380. elif len(lFiles) == 1:
  381. # Wonderful. Let's do the PDF-Comparison
  382. lPDFDataClass = PDFCompareDetails()
  383. lPDFDataClass.fileName = lFiles[0][0]
  384. lPDFDataClass.referenceID = lValue
  385. lDict = {"": lPDFDataClass}
  386. lPDFCompare = PDFCompare()
  387. lDict = lPDFCompare.compare_multiple(lDict)
  388. self.testcaseDataDict[lFieldnameForResults + "_Status"] = lDict[""].Status
  389. self.testcaseDataDict[lFieldnameForResults + "_Results"] = lDict[""].StatusText
  390. def replaceAllVariables(self, lValue, lValue2, replaceFromDict=None):
  391. # Replace variables from data file
  392. try:
  393. if len(lValue) > 0:
  394. lValue = self.replaceVariables(lValue, replaceFromDict=replaceFromDict)
  395. if len(lValue2) > 0:
  396. lValue2 = self.replaceVariables(lValue2, replaceFromDict=replaceFromDict)
  397. except Exception as ex:
  398. logger.warning(f"During replacement of variables an error happened: {ex}")
  399. return lValue, lValue2
  400. def __getIBAN(self, lValue, lValue2):
  401. from baangt.base.IBAN import IBAN
  402. if not lValue2:
  403. logger.critical("IBAN-Method called without destination field name in column 'Value 2'")
  404. return
  405. lIBAN = IBAN()
  406. self.testcaseDataDict[lValue2] = lIBAN.getRandomIBAN()
  407. pass
  408. def checkLinks(self):
  409. """
  410. Will check all links on the current webpage
  411. Result will be written into "CheckedLinks" in TestDataDict
  412. If theres a returncode >= 400 in the list, we'll mark the testcase as failed
  413. """
  414. if self.testcaseDataDict.get("CheckedLinks"):
  415. self.testcaseDataDict["Result_CheckedLinks"] += self.browserSession.checkLinks()
  416. else:
  417. self.testcaseDataDict["Result_CheckedLinks"] = self.browserSession.checkLinks()
  418. for entry in self.testcaseDataDict["Result_CheckedLinks"]:
  419. if entry[0] > 400:
  420. self.testcaseDataDict[GC.TESTCASESTATUS] = GC.TESTCASESTATUS_ERROR
  421. self.testcaseDataDict[GC.TESTCASEERRORLOG] = self.testcaseDataDict.get(GC.TESTCASEERRORLOG, "") \
  422. + "URL-Checker error"
  423. break
  424. @staticmethod
  425. def ifQualifyForExecution(version_global, version_line):
  426. """ This function will test version_global and version_line
  427. @return True or False
  428. """
  429. if not version_global.strip():
  430. # No value is defined in Release, return True
  431. return True
  432. if not version_line.strip():
  433. # we skipped this line
  434. return True
  435. # split the version line
  436. if not len(version_line.strip().split(" ")) == 2:
  437. logger.debug(f"Invalid release format {version_line} ")
  438. return True
  439. comp_operator, version = version_line.strip().split(" ")
  440. if comp_operator == "<":
  441. return parse_version(version_global) < parse_version(version)
  442. elif comp_operator == ">":
  443. return parse_version(version_global) > parse_version(version)
  444. elif comp_operator == ">=":
  445. return parse_version(version_global) >= parse_version(version)
  446. elif comp_operator == "<=":
  447. return parse_version(version_global) <= parse_version(version)
  448. elif comp_operator == "=" or comp_operator == "==":
  449. return parse_version(version_global) == parse_version(version)
  450. else:
  451. logger.debug(f"Global version {version_global}, line version {version_line} ")
  452. return False
  453. def doSaveData(self, toField, valueForField, lLocatorType, lLocator):
  454. """
  455. Save fields. Either from an existing DICT (usually in API-Mode) or from a Webelement (in Browser-Mode)
  456. :param toField:
  457. :param valueForField:
  458. :param lLocatorType:
  459. :param lLocator:
  460. :return: no return parameter. The implicit return is a value in a field.
  461. """
  462. if self.testCase[1][GC.KWARGS_TESTCASETYPE] == GC.KWARGS_BROWSER:
  463. xpath, css, id = TestStepMaster.__setLocator(lLocatorType, lLocator)
  464. self.testcaseDataDict[toField] = self.browserSession.findByAndWaitForValue(xpath=xpath, css=css, id=id)
  465. logger.debug(f"Saved {self.testcaseDataDict[toField]} to {toField}")
  466. elif self.testCase[1][GC.KWARGS_TESTCASETYPE] == GC.KWARGS_API_SESSION:
  467. self.testcaseDataDict[toField] = valueForField
  468. else:
  469. sys.exit("Testcase Type not supported")
  470. @staticmethod
  471. def __setLocator(lLocatorType, lLocator):
  472. return utils.setLocatorFromLocatorType(lLocatorType, lLocator)
  473. @staticmethod
  474. def _setTimeout(lTimeout):
  475. return 20 if not lTimeout else float(lTimeout)
  476. def __doComparisons(self, lComparison, value1, value2):
  477. if isinstance(value1, bool) or isinstance(value2, bool):
  478. value1 = utils.anything2Boolean(value1)
  479. value2 = utils.anything2Boolean(value2)
  480. if value2 == 'None':
  481. value2 = None
  482. logger.debug(f"Evaluating IF-Condition: Value1 = {value1}, comparison={lComparison}, value2={value2}")
  483. if lComparison == "=":
  484. if value1 == value2:
  485. self.manageNestedCondition(condition="IF", ifis=True)
  486. else:
  487. self.manageNestedCondition(condition="IF", ifis=False)
  488. elif lComparison == "!=":
  489. if value1 != value2:
  490. self.manageNestedCondition(condition="IF", ifis=True)
  491. else:
  492. self.manageNestedCondition(condition="IF", ifis=False)
  493. elif lComparison == ">":
  494. if value1 > value2:
  495. self.manageNestedCondition(condition="IF", ifis=True)
  496. else:
  497. self.manageNestedCondition(condition="IF", ifis=False)
  498. elif lComparison == "<":
  499. if value1 < value2:
  500. self.manageNestedCondition(condition="IF", ifis=True)
  501. else:
  502. self.manageNestedCondition(condition="IF", ifis=False)
  503. elif lComparison == ">=":
  504. if value1 >= value2:
  505. self.manageNestedCondition(condition="IF", ifis=True)
  506. else:
  507. self.manageNestedCondition(condition="IF", ifis=False)
  508. elif lComparison == "<=":
  509. if value1 <= value2:
  510. self.manageNestedCondition(condition="IF", ifis=True)
  511. else:
  512. self.manageNestedCondition(condition="IF", ifis=False)
  513. elif lComparison.upper() == "IP": # Is Part of (Value 1 is part of Value 2)
  514. if value1 in value2:
  515. self.manageNestedCondition(condition="IF", ifis=True)
  516. else:
  517. self.manageNestedCondition(condition="IF", ifis=False)
  518. elif lComparison.upper() == 'CO': # COntains (Value 1 contains Value 2)
  519. if value2 in value1:
  520. self.manageNestedCondition(condition="IF", ifis=True)
  521. else:
  522. self.manageNestedCondition(condition="IF", ifis=False)
  523. elif not lComparison: # Check only, if Value1 has a value.
  524. val = True if value1 else False
  525. self.manageNestedCondition(condition="IF", ifis=val)
  526. else:
  527. raise BaseException(f"Comparison Operator not supported/unknown {lComparison}")
  528. return self.ifIsTrue
  529. def execute(self):
  530. """Method is overwritten in all children/subclasses"""
  531. pass
  532. def teardown(self):
  533. if self.testCaseStatus:
  534. if not self.testcaseDataDict[GC.TESTCASESTATUS]:
  535. self.testcaseDataDict[GC.TESTCASESTATUS] = self.testCaseStatus
  536. elif self.testCaseStatus == GC.TESTCASESTATUS_SUCCESS:
  537. # We'll not overwrite a potential Error Status of the testcase
  538. pass
  539. elif self.testCaseStatus == GC.TESTCASESTATUS_ERROR:
  540. self.testcaseDataDict = GC.TESTCASESTATUS_ERROR
  541. else:
  542. sys.exit("No idea, what happened here. Unknown condition appeared")
  543. self.timing.takeTime(self.timingName)
  544. def replaceVariables(self, expression, replaceFromDict=None):
  545. """
  546. The syntax for variables is currently $(<column_name_from_data_file>). Multiple variables can be assigned
  547. in one cell, for instance perfectly fine: "http://$(BASEURL)/$(ENDPOINT)"
  548. There's a special syntax for the faker module: $(FAKER.<fakermethod>).
  549. Also a special syntax for API-Handling: $(APIHandling.<DictElementName>).
  550. @param expression: the current cell, either as fixed value, e.g. "Franzi" or with a varible $(DATE)
  551. @return: the replaced value, e.g. if expression was $(DATE) and the value in column "DATE" of data-file was
  552. "01.01.2020" then return will be "01.01.2020"
  553. """
  554. if "$(" not in expression:
  555. return expression
  556. while "$(" in expression:
  557. if expression[0:2] == "$(":
  558. left_part = ""
  559. else:
  560. left_part = expression.split("$(")[0]
  561. center = expression[len(left_part) + 2:]
  562. center = center.split(")")[0]
  563. right_part = expression[len(left_part) + len(center) + 3:]
  564. centerValue = ""
  565. if replaceFromDict: # json is supplied with repeat tag, that json is used here to get main data
  566. dic = replaceFromDict
  567. for key in center.split('.')[-1:]:
  568. dic = self.iterate_json(dic, key)
  569. centerValue = dic
  570. if "random{" in center.lower() and "}" in center: # random keyword is processed here
  571. args = json.loads(center[6:])
  572. # dictionary used in converting data in to real parameters which are used by the method
  573. change_args = {"type": "RandomizationType", "min": "mini", "max": "maxi", "format": "format"}
  574. data = {} # dictionary containing arguments for method and will be used as **args
  575. for keys in args:
  576. if keys not in change_args: # if key is not a valid argument for method
  577. logger.info(f'"{keys}" is not a valid argument for Random data generator. So it is not used')
  578. else:
  579. data[change_args[keys]] = args[keys]
  580. centerValue = self.randomValues.retrieveRandomValue(**data)
  581. if centerValue: # if we got value from the passed json then bypass this if else conditions
  582. pass
  583. elif "." not in center:
  584. # Replace the variable with the value from data structure
  585. centerValue = self.testcaseDataDict.get(center)
  586. else:
  587. # This is a reference to a DICT with ".": for instance APIHandling.AnswerContent("<bla>")
  588. dictVariable = center.split(".")[0]
  589. dictValue = center.split(".")[1]
  590. if dictVariable == 'ANSWER_CONTENT':
  591. centerValue = self.apiSession.session[1].answerJSON.get(dictValue, "Empty")
  592. elif dictVariable == 'FAKER':
  593. # This is to call Faker Module with the Method, that is given after the .
  594. centerValue = self._getFakerData(dictValue)
  595. elif self.testcaseDataDict.get(dictVariable):
  596. dic = self.testcaseDataDict.get(dictVariable)
  597. for key in center.split('.')[1:]:
  598. dic = self.iterate_json(dic, key)
  599. centerValue = dic
  600. else:
  601. raise BaseException(f"Missing code to replace value for: {center}")
  602. if not centerValue:
  603. if center in self.testcaseDataDict.keys():
  604. # The variable exists, but has no value.
  605. centerValue = ""
  606. else:
  607. raise BaseException(f"Variable not found: {center}, input parameter was: {expression}")
  608. if not isinstance(centerValue, list) and not isinstance(centerValue, dict):
  609. expression = "".join([left_part, str(centerValue), right_part])
  610. else:
  611. expression = centerValue
  612. if type(expression) == float or type(expression) == int:
  613. expression = str(int(expression))
  614. return expression
  615. def iterate_json(self, data, key):
  616. # itereate through list of json and create list of data from dictionary inside those list
  617. if type(data) is list:
  618. lis = []
  619. for dt in data:
  620. dt = self.iterate_json(dt, key)
  621. lis.append(dt)
  622. return lis
  623. elif type(data) is dict:
  624. return data.get(key)
  625. def _getFakerData(self, fakerMethod):
  626. if not self.baangtFaker:
  627. self.baangtFaker = baangtFaker()
  628. logger.debug(f"Calling faker with method: {fakerMethod}")
  629. return self.baangtFaker.fakerProxy(fakerMethod=fakerMethod)