TestStepMaster.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  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 settext(self, **kwargs):
  241. self.browserSession.findByAndSetText(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  242. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  243. def settextif(self, **kwargs):
  244. self.browserSession.findByAndSetTextIf(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  245. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  246. def forcetext(self, **kwargs):
  247. self.browserSession.findByAndForceText(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  248. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  249. def forcetextif(self, **kwargs):
  250. if kwargs["lValue"]:
  251. self.browserSession.findByAndForceText(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  252. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  253. def forcetextjs(self, **kwargs):
  254. if kwargs["lValue"]:
  255. self.browserSession.findByAndForceViaJS(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], value=kwargs["lValue"],
  256. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  257. def setanchor(self, **kwargs):
  258. if not kwargs["lLocator"]:
  259. self.anchor = None
  260. self.anchorLocator = None
  261. self.anchorLocatorType = None
  262. else:
  263. found = self.browserSession.findBy(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], timeout=kwargs["lTimeout"])
  264. if found:
  265. self.anchor = self.browserSession.element
  266. self.anchorLocator = kwargs["lLocator"]
  267. self.anchorLocatorType = kwargs["lLocatorType"]
  268. logger.debug(f'Anchor set: {kwargs["lLocatorType"]} {kwargs["lLocator"]}')
  269. else:
  270. logger.error(f'Anchor should be set, but can\'t be found in the current page: {kwargs["lLocatorType"]}, {kwargs["lLocator"]}')
  271. raise ValueError(f'Anchor should be set, but can\'t be found in the current page: {kwargs["lLocatorType"]}, {kwargs["lLocator"]}')
  272. def handleiframe(self, **kwargs):
  273. self.browserSession.handleIframe(kwargs["lLocator"])
  274. def switchwindow(self, **kwargs):
  275. lWindow = kwargs["lValue"]
  276. if lWindow.isnumeric():
  277. lWindow = int(lWindow)
  278. self.browserSession.handleWindow(windowNumber=lWindow, timeout=kwargs["lTimeout"])
  279. def click(self, **kwargs):
  280. self.browserSession.findByAndClick(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"],
  281. timeout=kwargs["lTimeout"], optional=kwargs["lOptional"])
  282. def clickif(self, **kwargs):
  283. self.browserSession.findByAndClickIf(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"],
  284. timeout=kwargs["lTimeout"], value=kwargs["lValue"], optional=kwargs["lOptional"])
  285. def pause(self, **kwargs):
  286. self.browserSession.sleep(seconds=float(kwargs["lValue"]))
  287. def if_(self, **kwargs):
  288. # Originally we had only Comparisons. Now we also want to check for existance of Field
  289. if not kwargs["lValue"] and kwargs["lLocatorType"] and kwargs["lLocator"]:
  290. kwargs["lValue"] = self.browserSession.findBy(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"], optional=kwargs["lOptional"],
  291. timeout=kwargs["lTimeout"])
  292. self.__doComparisons(kwargs["lComparison"], value1=kwargs["lValue"], value2=kwargs["lValue"])
  293. logger.debug(f'IF-condition original Value: {kwargs["original_value"]} (transformed: {kwargs["lValue"]}) '
  294. f'{kwargs["lComparison"]} {kwargs["lValue"]} evaluated to: {self.ifIsTrue} ')
  295. def else_(self, **kwargs):
  296. if not self.ifIsTrue: # don't run else statement if "if" statement is true
  297. self.manageNestedCondition(condition=kwargs["lActivity"])
  298. logger.debug("Executing ELSE-condition")
  299. else:
  300. self.ifIsTrue = False
  301. def endif(self, **kwargs):
  302. self.manageNestedCondition(condition=kwargs["lActivity"])
  303. def repeat(self, **kwargs):
  304. self.repeatActive += 1
  305. self.repeatIsTrue = True
  306. self.repeatCommands.append({})
  307. if kwargs["original_value"] not in self.testRunInstance.json_dict:
  308. self.testRunInstance.json_dict[kwargs["original_value"]] = []
  309. self.testRunInstance.json_dict[kwargs["original_value"]].append(kwargs["lValue"])
  310. self.repeatReplaceDataDictionary.append(kwargs["lValue"])
  311. self.repeatCount.append(kwargs["lValue"])
  312. def goback(self, **kwargs):
  313. self.browserSession.goBack()
  314. def apiurl(self, **kwargs):
  315. self.apiSession.setBaseURL(kwargs["lValue"])
  316. def endpoint(self, **kwargs):
  317. self.apiSession.setEndPoint(kwargs["lValue"])
  318. def post(self, **kwargs):
  319. self.apiSession.postURL(content=kwargs["lValue"])
  320. def get(self, **kwargs):
  321. self.apiSession.getURL()
  322. def header(self, **kwargs):
  323. self.apiSession.setHeaders(setHeaderData=kwargs["lValue"])
  324. def save(self, **kwargs):
  325. self.doSaveData(kwargs["lValue"], kwargs["lValue"], kwargs["lLocatorType"], kwargs["lLocator"])
  326. def clear(self, **kwargs):
  327. # Clear a variable:
  328. if self.testcaseDataDict.get(kwargs["lValue"]):
  329. del self.testcaseDataDict[kwargs["lValue"]]
  330. def saveto(self, **kwargs):
  331. # In this case, we need to parse the real field, not the representation of the replaced field value
  332. self.doSaveData(kwargs["command"]['Value'], kwargs["lValue"], kwargs["lLocatorType"], kwargs["lLocator"])
  333. def submit(self, **kwargs):
  334. self.browserSession.submit()
  335. def address_create(self, **kwargs):
  336. # Create Address with option kwargs["lValue"] and kwargs["lValue"]
  337. AddressCreate(kwargs["lValue"], kwargs["lValue"])
  338. # Update testcaseDataDict with addressDict returned from
  339. AddressCreate.returnAddress()
  340. self.testcaseDataDict.update(AddressCreate.returnAddress())
  341. def assert_(self, **kwargs):
  342. value_found = self.browserSession.findByAndWaitForValue(xpath=kwargs["xpath"], css=kwargs["css"], id=kwargs["id"],
  343. optional=kwargs["lOptional"], timeout=kwargs["lTimeout"])
  344. if not self.__doComparisons(kwargs["lComparison"], value1=value_found, value2=kwargs["lValue"]):
  345. logger.error(f'Condition {value_found} {kwargs["lComparison"]} {kwargs["lValue"]} was not met during assert.')
  346. raise baangtTestStepException(f'Expected Value: {kwargs["lValue"]}, Value found :{value_found} ')
  347. def iban(self, **kwargs):
  348. # Create Random IBAN. Value1 = Input-Parameter for IBAN-Function. Value2=Fieldname
  349. self.__getIBAN(kwargs["lValue"], kwargs["lValue2"])
  350. def pdfcompare(self, **kwargs):
  351. self.doPDFComparison(kwargs["lValue"])
  352. def checklinks(self, **kwargs):
  353. self.checkLinks()
  354. def alertif(self, **kwargs):
  355. self.browserSession.confirmAlertIfAny()
  356. def tcstoptestcase(self, **kwargs):
  357. self.testcaseDataDict[GC.TESTCASESTATUS_STOP] = "X" # will stop the test case
  358. def tcstoptestcaseerror(self, **kwargs):
  359. self.testcaseDataDict[GC.TESTCASESTATUS_STOPERROR] = "X" # will stop the test case and set error
  360. def _extractAllSingleValues(self, command):
  361. lActivity = command["Activity"].upper()
  362. lLocatorType = command["LocatorType"].upper()
  363. try:
  364. lLocator = self.replaceVariables(command["Locator"])
  365. except Exception as ex:
  366. logger.info(ex)
  367. if lLocator and not lLocatorType: # If locatorType is empty, default it to XPATH
  368. lLocatorType = 'XPATH'
  369. xpath, css, id = self.__setLocator(lLocatorType, lLocator)
  370. return css, id, lActivity, lLocator, lLocatorType, xpath
  371. def doPDFComparison(self, lValue, lFieldnameForResults="DOC_Compare"):
  372. lFiles = self.browserSession.findNewFiles()
  373. if len(lFiles) > 1:
  374. # fixme: Do something! There were more than 1 files since last check. Damn
  375. logger.critical(f"There were {len(lFiles)} files new since last check. Can't handle that. ")
  376. raise Exception
  377. elif len(lFiles) == 1:
  378. # Wonderful. Let's do the PDF-Comparison
  379. lPDFDataClass = PDFCompareDetails()
  380. lPDFDataClass.fileName = lFiles[0][0]
  381. lPDFDataClass.referenceID = lValue
  382. lDict = {"": lPDFDataClass}
  383. lPDFCompare = PDFCompare()
  384. lDict = lPDFCompare.compare_multiple(lDict)
  385. self.testcaseDataDict[lFieldnameForResults + "_Status"] = lDict[""].Status
  386. self.testcaseDataDict[lFieldnameForResults + "_Results"] = lDict[""].StatusText
  387. def replaceAllVariables(self, lValue, lValue2, replaceFromDict=None):
  388. # Replace variables from data file
  389. try:
  390. if len(lValue) > 0:
  391. lValue = self.replaceVariables(lValue, replaceFromDict=replaceFromDict)
  392. if len(lValue2) > 0:
  393. lValue2 = self.replaceVariables(lValue2, replaceFromDict=replaceFromDict)
  394. except Exception as ex:
  395. logger.warning(f"During replacement of variables an error happened: {ex}")
  396. return lValue, lValue2
  397. def __getIBAN(self, lValue, lValue2):
  398. from baangt.base.IBAN import IBAN
  399. if not lValue2:
  400. logger.critical("IBAN-Method called without destination field name in column 'Value 2'")
  401. return
  402. lIBAN = IBAN()
  403. self.testcaseDataDict[lValue2] = lIBAN.getRandomIBAN()
  404. pass
  405. def checkLinks(self):
  406. """
  407. Will check all links on the current webpage
  408. Result will be written into "CheckedLinks" in TestDataDict
  409. If theres a returncode >= 400 in the list, we'll mark the testcase as failed
  410. """
  411. if self.testcaseDataDict.get("CheckedLinks"):
  412. self.testcaseDataDict["Result_CheckedLinks"] += self.browserSession.checkLinks()
  413. else:
  414. self.testcaseDataDict["Result_CheckedLinks"] = self.browserSession.checkLinks()
  415. for entry in self.testcaseDataDict["Result_CheckedLinks"]:
  416. if entry[0] > 400:
  417. self.testcaseDataDict[GC.TESTCASESTATUS] = GC.TESTCASESTATUS_ERROR
  418. self.testcaseDataDict[GC.TESTCASEERRORLOG] = self.testcaseDataDict.get(GC.TESTCASEERRORLOG, "") \
  419. + "URL-Checker error"
  420. break
  421. @staticmethod
  422. def ifQualifyForExecution(version_global, version_line):
  423. """ This function will test version_global and version_line
  424. @return True or False
  425. """
  426. if not version_global.strip():
  427. # No value is defined in Release, return True
  428. return True
  429. if not version_line.strip():
  430. # we skipped this line
  431. return True
  432. # split the version line
  433. if not len(version_line.strip().split(" ")) == 2:
  434. logger.debug(f"Invalid release format {version_line} ")
  435. return True
  436. comp_operator, version = version_line.strip().split(" ")
  437. if comp_operator == "<":
  438. return parse_version(version_global) < parse_version(version)
  439. elif comp_operator == ">":
  440. return parse_version(version_global) > parse_version(version)
  441. elif comp_operator == ">=":
  442. return parse_version(version_global) >= parse_version(version)
  443. elif comp_operator == "<=":
  444. return parse_version(version_global) <= parse_version(version)
  445. elif comp_operator == "=" or comp_operator == "==":
  446. return parse_version(version_global) == parse_version(version)
  447. else:
  448. logger.debug(f"Global version {version_global}, line version {version_line} ")
  449. return False
  450. def doSaveData(self, toField, valueForField, lLocatorType, lLocator):
  451. """
  452. Save fields. Either from an existing DICT (usually in API-Mode) or from a Webelement (in Browser-Mode)
  453. :param toField:
  454. :param valueForField:
  455. :param lLocatorType:
  456. :param lLocator:
  457. :return: no return parameter. The implicit return is a value in a field.
  458. """
  459. if self.testCase[1][GC.KWARGS_TESTCASETYPE] == GC.KWARGS_BROWSER:
  460. xpath, css, id = TestStepMaster.__setLocator(lLocatorType, lLocator)
  461. self.testcaseDataDict[toField] = self.browserSession.findByAndWaitForValue(xpath=xpath, css=css, id=id)
  462. logger.debug(f"Saved {self.testcaseDataDict[toField]} to {toField}")
  463. elif self.testCase[1][GC.KWARGS_TESTCASETYPE] == GC.KWARGS_API_SESSION:
  464. self.testcaseDataDict[toField] = valueForField
  465. else:
  466. sys.exit("Testcase Type not supported")
  467. @staticmethod
  468. def __setLocator(lLocatorType, lLocator):
  469. return utils.setLocatorFromLocatorType(lLocatorType, lLocator)
  470. @staticmethod
  471. def _setTimeout(lTimeout):
  472. return 20 if not lTimeout else float(lTimeout)
  473. def __doComparisons(self, lComparison, value1, value2):
  474. if isinstance(value1, bool) or isinstance(value2, bool):
  475. value1 = utils.anything2Boolean(value1)
  476. value2 = utils.anything2Boolean(value2)
  477. if value2 == 'None':
  478. value2 = None
  479. logger.debug(f"Evaluating IF-Condition: Value1 = {value1}, comparison={lComparison}, value2={value2}")
  480. if lComparison == "=":
  481. if value1 == value2:
  482. self.manageNestedCondition(condition="IF", ifis=True)
  483. else:
  484. self.manageNestedCondition(condition="IF", ifis=False)
  485. elif lComparison == "!=":
  486. if value1 != value2:
  487. self.manageNestedCondition(condition="IF", ifis=True)
  488. else:
  489. self.manageNestedCondition(condition="IF", ifis=False)
  490. elif lComparison == ">":
  491. if value1 > value2:
  492. self.manageNestedCondition(condition="IF", ifis=True)
  493. else:
  494. self.manageNestedCondition(condition="IF", ifis=False)
  495. elif lComparison == "<":
  496. if value1 < value2:
  497. self.manageNestedCondition(condition="IF", ifis=True)
  498. else:
  499. self.manageNestedCondition(condition="IF", ifis=False)
  500. elif lComparison == ">=":
  501. if value1 >= value2:
  502. self.manageNestedCondition(condition="IF", ifis=True)
  503. else:
  504. self.manageNestedCondition(condition="IF", ifis=False)
  505. elif lComparison == "<=":
  506. if value1 <= value2:
  507. self.manageNestedCondition(condition="IF", ifis=True)
  508. else:
  509. self.manageNestedCondition(condition="IF", ifis=False)
  510. elif lComparison.upper() == "IP": # Is Part of (Value 1 is part of Value 2)
  511. if value1 in value2:
  512. self.manageNestedCondition(condition="IF", ifis=True)
  513. else:
  514. self.manageNestedCondition(condition="IF", ifis=False)
  515. elif lComparison.upper() == 'CO': # COntains (Value 1 contains Value 2)
  516. if value2 in value1:
  517. self.manageNestedCondition(condition="IF", ifis=True)
  518. else:
  519. self.manageNestedCondition(condition="IF", ifis=False)
  520. elif not lComparison: # Check only, if Value1 has a value.
  521. val = True if value1 else False
  522. self.manageNestedCondition(condition="IF", ifis=val)
  523. else:
  524. raise BaseException(f"Comparison Operator not supported/unknown {lComparison}")
  525. return self.ifIsTrue
  526. def execute(self):
  527. """Method is overwritten in all children/subclasses"""
  528. pass
  529. def teardown(self):
  530. if self.testCaseStatus:
  531. if not self.testcaseDataDict[GC.TESTCASESTATUS]:
  532. self.testcaseDataDict[GC.TESTCASESTATUS] = self.testCaseStatus
  533. elif self.testCaseStatus == GC.TESTCASESTATUS_SUCCESS:
  534. # We'll not overwrite a potential Error Status of the testcase
  535. pass
  536. elif self.testCaseStatus == GC.TESTCASESTATUS_ERROR:
  537. self.testcaseDataDict = GC.TESTCASESTATUS_ERROR
  538. else:
  539. sys.exit("No idea, what happened here. Unknown condition appeared")
  540. self.timing.takeTime(self.timingName)
  541. def replaceVariables(self, expression, replaceFromDict=None):
  542. """
  543. The syntax for variables is currently $(<column_name_from_data_file>). Multiple variables can be assigned
  544. in one cell, for instance perfectly fine: "http://$(BASEURL)/$(ENDPOINT)"
  545. There's a special syntax for the faker module: $(FAKER.<fakermethod>).
  546. Also a special syntax for API-Handling: $(APIHandling.<DictElementName>).
  547. @param expression: the current cell, either as fixed value, e.g. "Franzi" or with a varible $(DATE)
  548. @return: the replaced value, e.g. if expression was $(DATE) and the value in column "DATE" of data-file was
  549. "01.01.2020" then return will be "01.01.2020"
  550. """
  551. if "$(" not in expression:
  552. return expression
  553. while "$(" in expression:
  554. if expression[0:2] == "$(":
  555. left_part = ""
  556. else:
  557. left_part = expression.split("$(")[0]
  558. center = expression[len(left_part) + 2:]
  559. center = center.split(")")[0]
  560. right_part = expression[len(left_part) + len(center) + 3:]
  561. centerValue = ""
  562. if replaceFromDict: # json is supplied with repeat tag, that json is used here to get main data
  563. dic = replaceFromDict
  564. for key in center.split('.')[-1:]:
  565. dic = self.iterate_json(dic, key)
  566. centerValue = dic
  567. if "random{" in center.lower() and "}" in center: # random keyword is processed here
  568. args = json.loads(center[6:])
  569. # dictionary used in converting data in to real parameters which are used by the method
  570. change_args = {"type": "RandomizationType", "min": "mini", "max": "maxi", "format": "format"}
  571. data = {} # dictionary containing arguments for method and will be used as **args
  572. for keys in args:
  573. if keys not in change_args: # if key is not a valid argument for method
  574. logger.info(f'"{keys}" is not a valid argument for Random data generator. So it is not used')
  575. else:
  576. data[change_args[keys]] = args[keys]
  577. centerValue = self.randomValues.retrieveRandomValue(**data)
  578. if centerValue: # if we got value from the passed json then bypass this if else conditions
  579. pass
  580. elif "." not in center:
  581. # Replace the variable with the value from data structure
  582. centerValue = self.testcaseDataDict.get(center)
  583. else:
  584. # This is a reference to a DICT with ".": for instance APIHandling.AnswerContent("<bla>")
  585. dictVariable = center.split(".")[0]
  586. dictValue = center.split(".")[1]
  587. if dictVariable == 'ANSWER_CONTENT':
  588. centerValue = self.apiSession.session[1].answerJSON.get(dictValue, "Empty")
  589. elif dictVariable == 'FAKER':
  590. # This is to call Faker Module with the Method, that is given after the .
  591. centerValue = self._getFakerData(dictValue)
  592. elif self.testcaseDataDict.get(dictVariable):
  593. dic = self.testcaseDataDict.get(dictVariable)
  594. for key in center.split('.')[1:]:
  595. dic = self.iterate_json(dic, key)
  596. centerValue = dic
  597. else:
  598. raise BaseException(f"Missing code to replace value for: {center}")
  599. if not centerValue:
  600. if center in self.testcaseDataDict.keys():
  601. # The variable exists, but has no value.
  602. centerValue = ""
  603. else:
  604. raise BaseException(f"Variable not found: {center}, input parameter was: {expression}")
  605. if not isinstance(centerValue, list) and not isinstance(centerValue, dict):
  606. expression = "".join([left_part, str(centerValue), right_part])
  607. else:
  608. expression = centerValue
  609. if type(expression) == float or type(expression) == int:
  610. expression = str(int(expression))
  611. return expression
  612. def iterate_json(self, data, key):
  613. # itereate through list of json and create list of data from dictionary inside those list
  614. if type(data) is list:
  615. lis = []
  616. for dt in data:
  617. dt = self.iterate_json(dt, key)
  618. lis.append(dt)
  619. return lis
  620. elif type(data) is dict:
  621. return data.get(key)
  622. def _getFakerData(self, fakerMethod):
  623. if not self.baangtFaker:
  624. self.baangtFaker = baangtFaker()
  625. logger.debug(f"Calling faker with method: {fakerMethod}")
  626. return self.baangtFaker.fakerProxy(fakerMethod=fakerMethod)