3 Star 5 Fork 0

Ricky/SeleniumTestRunner

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
SeleniumTestRunner.py 20.44 KB
一键复制 编辑 原始数据 按行查看 历史
Ricky 提交于 2021-01-18 14:05 +08:00 . add promte after done
import json
import os
import re
import shutil
import time
from copy import deepcopy
from typing import Final
import allure
import pytest
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.webelement import WebElement
from enums.EngineActIdEnum import EngineActIdEnum
from enums.ErrSkipMsgEnum import ErrSkipMsgEnum
from SeleniumHelper import SeleniumHelper
from faker import Faker
def tplTestFunc(self):
titleIdx = self.__class__.runner.currentTitleIdx
self.__class__.runner.currentTitleIdx += 1
self.__class__.runner.runTitle(titleIdx)
def runPyTest(dir: str, files: list[str], skipPyTest: bool, genAllureReport: bool):
reportDir = os.path.join(dir, 'report')
allureReportDir = os.path.abspath(os.path.join(dir, 'allure-report'))
if not skipPyTest:
try:
shutil.rmtree(reportDir)
except:
pass
agvs = [os.path.join(dir, iFile) for iFile in files]
agvs.extend(['-s', '-q', "--alluredir", reportDir])
pytest.main(agvs)
if genAllureReport:
allure_cmd = f"allure generate {reportDir} --clean -o {allureReportDir}" # 将报告转换成html格式文件的命令
os.system(allure_cmd)
print(f"\nallure-report dir : {allureReportDir}")
for iFile in files:
os.remove(os.path.join(dir, iFile))
print('done.')
# allure_cmd2 = f"allure open {allureReportDir}" # 将报告转换成html格式文件的命令
# os.system(allure_cmd2)
class SeleniumTestRunner:
VERSION: Final[list[int]] = [0, 0, 1]
DOC_TYPE: Final[str] = 'SeleniumTestRunner-Plan-DDT'
__argsReg: Final = re.compile(r"{{ARGS:(.*?)}}")
__varsReg: Final = re.compile(r"{{VARS:(.*?)}}")
__fakerReg: Final = re.compile(r"{{FAKER\.(.*?):?((?<=:).*?)?}}")
__keysReg: Final = re.compile(r"{{Keys\.(.*?)}}")
def __init__(self):
self.doc: dict = None
self.initSteps: list[dict] = None
self.titleLst: list[dict] = None
# self.titleRange: list = []
self.currentTitleIdx: int = 0
self.file: str = None
self.fake: Faker = None
self.screenshotOption: dict[str, int] = None
self.__seleniumHelper: SeleniumHelper = None
self.__vars: dict[str, any] = dict()
self.exitIfSetupErr: bool = False
self.skipModuleReason: str = ''
def _resetVars(self):
self.__vars.clear()
@staticmethod
def loadAll(dir: str, skipPyTest: bool, genAllureReport: bool) -> None:
files = os.listdir(dir)
jsonFiles: list[str] = []
plans: dict[str, dict] = {}
with open('TplOfTestPy.pyTpl', 'r', encoding="utf-8") as f:
tplStr: str = f.read()
pyFiles: list[str] = []
for iFileName in files:
iFileName2 = os.path.join(dir, iFileName)
if os.path.isfile(iFileName2):
d = os.path.splitext(iFileName)
if d[1].lower() == ".json":
absFile = os.path.abspath(iFileName2)
with open(absFile, 'r', encoding='utf-8') as fs:
doc: dict = json.load(fs)
if not SeleniumTestRunner.__checkDocVersion(doc):
continue
jsonFiles.append(absFile)
plans[d[0]] = doc
pyName = f'test_{d[0]}.py'
pyFiles.append(pyName)
with open(os.path.join(dir, pyName), 'w', encoding="utf-8") as f:
s = tplStr.replace('{{DOC_FILE}}', iFileName)
s = s.replace('{{DOC_PATH}}', absFile)
f.write(s)
runPyTest(dir, pyFiles, skipPyTest, genAllureReport)
return
@staticmethod
def loadFile(file: str):
with open(file, 'r', encoding='utf-8') as fs:
doc: dict = json.load(fs)
if not SeleniumTestRunner.__checkDocVersion(doc):
return False
self = SeleniumTestRunner()
self.doc = doc
self.file = file
titleLst = self.titleLst = []
plan: dict = doc['plan']
setupSteps: list = doc.get('init').get('setupSteps')
if setupSteps:
self.initSteps = []
self.exitIfSetupErr = doc.get('init').get('exitIfSetupErr')
for iStep in setupSteps:
self.initSteps.append(iStep)
features = plan['features']
for iFeature in features:
stories = iFeature['stories']
for iStory in stories:
titles: list[dict] = iStory['titles']
for iTitle in titles:
iTitle['feature'] = iFeature.get('name')
iTitle['story'] = iStory.get('name')
repeat = iTitle.get('repeat', 1)
if repeat > 1:
titleName = iTitle['name']
iRepeat = 1
while iRepeat <= repeat:
iTitle['name'] = titleName + f' {iRepeat}/{repeat}'
titleLst.append(iTitle.copy())
iRepeat += 1
else:
titleLst.append(iTitle)
# self.titleRange = [''] * len(titleLst)
self.currentTitleIdx = 0
return self
@staticmethod
def __checkDocVersion(doc: dict) -> bool:
if doc.get('docType') != SeleniumTestRunner.DOC_TYPE:
return False
docLibVer: str = doc.get('engineVersion')
if not docLibVer:
return False
docLibVerLst: list[str] = docLibVer.split('.')
if len(docLibVerLst) != 3:
return False
verIdx = 0
while verIdx < 3:
if int(docLibVerLst[verIdx]) > SeleniumTestRunner.VERSION[verIdx]:
return False
verIdx += 1
return True
def initClass(self, cls: type):
nextTestIdx: int = 0
for iTitle in self.titleLst:
setattr(cls, 'test_' + str(nextTestIdx).zfill(5), tplTestFunc)
nextTestIdx += 1
def initRunner(self):
initDict: dict = self.doc['init']
self.currentTitleIdx = 0
with allure.step("--启动参数--"):
with allure.step(f'设定浏览器类型: {initDict["browserType"]}'):
browserType = initDict["browserType"]
implicitly_wait = initDict.get('implicitly_wait')
if implicitly_wait:
with allure.step(f'设定隐性等待: {implicitly_wait}秒'):
assert True
else:
implicitly_wait = 10
with allure.step(f'设定隐性等待为默认值: {implicitly_wait}秒'):
pass
options: list[str] = initDict.get('browserOptions')
if options and len(options) > 0:
with allure.step(f'设定浏览器启动选项: {len(options)}项'):
for iOption in options:
with allure.step(iOption):
assert True
pageLoadStrategy = initDict.get('pageLoadStrategy')
fakerLocal: str = initDict.get('fakerLocal') or "zh_CN"
with allure.step(f'设定Faker本地语言: {fakerLocal}'):
self.fake = Faker(locale=fakerLocal)
ssOption: dict = initDict.get('screenshot', dict({'before': 0, 'after': 2}))
ssBefore: int = ssOption.get('before', 0)
ssAfter: int = ssOption.get('after', 2)
with allure.step(f'设定截图策略: before={ssBefore} , after={ssAfter}'):
ssOption['before'] = ssBefore
ssOption['after'] = ssAfter
self.screenshotOption = ssOption
with allure.step("启动浏览器"):
self.__seleniumHelper = SeleniumHelper(browserType, options)
if self.initSteps:
with allure.step('--启动指令--'):
for iStep in self.initSteps:
try:
self.runStep(iStep)
except Exception as e:
if self.exitIfSetupErr:
self.skipModuleReason = "Module Setup 失败"
# pytest.fail("Module Setup 失败")
def runTitle(self, titleIdx: int):
title = self.titleLst[titleIdx]
severity: str = title.get('severity')
if severity:
allure.dynamic.severity(severity)
feature = title.get('feature')
if feature:
allure.dynamic.feature(feature)
story = title.get('story')
if story:
allure.dynamic.story(story)
allure.dynamic.title(title['name'])
if title.get('description'):
allure.dynamic.description_html(title.get('description'))
if title.get('tag'):
allure.dynamic.tag(*title.get('tag'))
if self.skipModuleReason:
with allure.step(self.skipModuleReason):
pytest.skip(self.skipModuleReason)
return
steps = title.get('steps')
errSkip: int = title.get('errSkip', 0)
try:
for iStep in steps:
self.runStep(iStep)
except Exception as e:
if errSkip == 0:
pytest.fail(e.args[0])
# raise e
elif errSkip == 2:
self.skipModuleReason = e.args[0]
pytest.fail(e.args[0])
# raise e
def quit(self):
self.__seleniumHelper.quit()
def runStep(self, step: dict):
item = list(step.items())[0]
act = item[0]
value: dict = item[1]
seleniumHelper = self.__seleniumHelper
stepName: str = value["step_name"]
stepNameV = self._getSysStrValue(stepName, value, False)
options: dict = value.get('_options', {})
ssOption: dict = options.get('screenshot', self.screenshotOption)
assertError: int = options.get('assertError', False)
errSkip: int = options.get('errSkip', 0)
def _runOneStep(step_name: str, currentRepeat: int, totalRepeat: int):
# region init
stepV: dict = deepcopy(value)
for iKey in stepV.keys():
if type(stepV[iKey]) == str:
stepV[iKey] = self._getSysStrValue(stepV[iKey], stepV, iKey != 'step_name')
isSubStep = totalRepeat > 1
ssBefore: int = ssOption.get('before', 0)
ssAfter: int = ssOption.get('after', 2)
beforeSS: bytes = None
if isSubStep:
step_name = step_name + f'\t{currentRepeat}/{totalRepeat}'
def _saveScreenshot(beforeSS: bytes, before: bool, after: bool):
if ssBefore != 0 and before:
allure.attach(beforeSS, "<Step Before>", attachment_type=allure.attachment_type.PNG)
if after:
afterSS: bytes = seleniumHelper.BROSWER_screenshot()
allure.attach(afterSS, "<Step After>", attachment_type=allure.attachment_type.PNG)
if assertError:
step_name = step_name + '\t[E]'
# endregion
with allure.step(step_name):
try:
if ssBefore != 0:
beforeSS: bytes = seleniumHelper.BROSWER_screenshot()
if act == EngineActIdEnum.sub_steps:
for iSubAction in stepV.get('actions'):
self.runStep(iSubAction)
elif act == EngineActIdEnum.SYS_sleep:
time.sleep(stepV.get('time'))
pass
elif act == EngineActIdEnum.ENGINE_setVar:
varName = stepV.get("name")
# varValue = self._getSysStrValue(stepV.get('value'))
varValue = stepV.get('value')
with allure.step(f'{varName} = {varValue}'):
self.__vars[varName] = varValue
elif act == EngineActIdEnum.BROWSER_getUrl:
seleniumHelper.BROWSER_getUrl(stepV.get('url'), stepV.get('force'))
elif act == EngineActIdEnum.BROWSER_switch_to_window:
seleniumHelper.BROWSER_switch_to_window(stepV.get('idx'))
elif act == EngineActIdEnum.BROWSER_wait:
self._waitBrowser(stepV)
elif act == EngineActIdEnum.BROWSER_screenshot:
png = seleniumHelper.BROSWER_screenshot()
allure.attach(png, "窗口截图", attachment_type=allure.attachment_type.PNG)
elif act == EngineActIdEnum.BROWSER_close:
seleniumHelper.BROWSER_close()
elif act == EngineActIdEnum.BROWSER_maximize_window:
seleniumHelper.BROWSER_maximize_window()
elif act == EngineActIdEnum.BROWSER_set_window_size:
seleniumHelper.BROWSER_set_window_size(stepV.get('width', 1280), stepV.get('height', 800))
elif act == EngineActIdEnum.BROWSER_SET_implicitly_wait:
seleniumHelper.BROWSER_SET_implicitly_wait(stepV.get('time'))
elif act == EngineActIdEnum.WEB_wait:
self._waitElem(stepV)
elif act == EngineActIdEnum.WEB_assertAttr:
elem = self.findElem(stepV)
elemValue: str = elem.get_attribute(stepV.get('attribute'))
pos = elemValue.find(stepV.get('value'))
with allure.step(f":内容发现位置:{pos}"):
assert pos > -1
elif act == EngineActIdEnum.WEB_sendKeys:
elem = self.findElem(stepV)
v = stepV.get('value')
seleniumHelper.WEB_scrollIntoView(elem)
if stepV.get('clear'):
with allure.step('清空已有内容'):
elem.clear()
with allure.step(f"输入值 : {v}"):
elem.send_keys(v)
elif act == EngineActIdEnum.WEB_click:
elem = self.findElem(stepV)
seleniumHelper.WEB_scrollIntoView(elem)
with allure.step("单击元素"):
elem.click()
elif act == EngineActIdEnum.WEB_select:
selectBy: str = stepV.get('selectBy')
selectContent: str = stepV.get('selectContent')
elem = self.findElem(stepV)
with allure.step(f"点选Item by={selectBy} , content={selectContent}"):
seleniumHelper.WEB_select(elem, selectBy, selectContent)
elif act == EngineActIdEnum.WEB_switch_to_frame:
seleniumHelper.WEB_switch_to_frame(stepV.get('by'), stepV.get('path'))
pass
elif act == EngineActIdEnum.WEB_switch_to_default_content:
seleniumHelper.WEB_switch_to_default_content()
pass
else:
raise NotImplementedError()
# except NotImplementedError as e:
# allure.attach(json.dump(value), "doc", attachment_type=allure.attachment_type.JSON)
# raise e
except Exception as e:
if isinstance(e, NotImplementedError):
allure.attach(json.dump(stepV), "doc", attachment_type=allure.attachment_type.JSON)
raise e
if not assertError:
_saveScreenshot(beforeSS, ssBefore != 0, ssAfter != 0)
if errSkip == 0:
msg = ErrSkipMsgEnum.Msg100
with allure.step(msg):
# pytest.fail(str(e))
raise Exception(msg, err=e, step=stepV)
elif errSkip == 1:
msg = ErrSkipMsgEnum.Msg101
with allure.step(msg):
pass
else:
msg = ErrSkipMsgEnum.Msg102
raise Exception(msg, err=e, step=stepV)
else:
with allure.step(ErrSkipMsgEnum.Msg000):
pass
else:
if assertError:
_saveScreenshot(beforeSS, ssBefore != 0, ssAfter != 0)
if errSkip == 0:
msg = ErrSkipMsgEnum.Msg010
with allure.step(msg):
raise Exception(msg, step=stepV)
elif errSkip == 1:
msg = ErrSkipMsgEnum.Msg011
with allure.step(msg):
pass
else:
msg = ErrSkipMsgEnum.Msg012
with allure.step(msg):
raise Exception(msg, step=stepV)
else:
_saveScreenshot(beforeSS, ssBefore == 1, ssAfter == 1)
repeat = options.get('repeat', 1)
if repeat > 1:
iRepeat = 1
# with allure.step(stepNameV + f'\t重复{repeat}次'):
while iRepeat <= repeat:
_runOneStep(stepNameV, iRepeat, repeat)
iRepeat += 1
else:
_runOneStep(stepNameV, 1, 1)
def findElem(self, act: dict) -> WebElement:
by: str = act.get('by') or By.XPATH
path: str = act.get('path')
waitOptions: dict = act.get('waitOptions')
if waitOptions:
with allure.step(f"[等待]定位元素 : by={by} , path={path} , wait={waitOptions.get('method')}"):
elem = self.__seleniumHelper.WEB_waitElem(waitOptions, by, path)
else:
with allure.step(f"定位元素 : by={by} , path={path}"):
elem = self.__seleniumHelper.elemFind(by, path)
return elem
def _waitElem(self, act: dict) -> WebElement:
by: str = act.get('by') or By.XPATH
path: str = act.get('path')
waitOptions: dict = act.get('waitOptions', {"timeout": 10, "until": "until", "frequency": 0.5, })
elem = self.__seleniumHelper.WEB_waitElem(waitOptions, by, path)
return elem
def _waitBrowser(self, act: dict) -> WebElement:
method: str = act.get("method")
timeout: float = act.get("timeout") or 10.0
until: bool = act.get('until', "until") == "until"
frequency: float = act.get('frequency') or 0.5
args: list = act.get('args', [])
if method == 'current_url_changes':
method = 'url_changes'
args = [self.__seleniumHelper.BROWSER_current_url()]
elem = self.__seleniumHelper.BROWSER_wait(method, timeout, frequency, until, args)
return elem
def _getFakeValue(self, old: str) -> str:
def _fakerRepl(matched: re):
funcName = matched.groups()[0]
func = getattr(self.fake, funcName)
argsStr = matched.groups()[1]
result = None
if argsStr:
args = json.loads(argsStr)
if type(args) == list:
result = func(*args)
elif type(args) == dict:
result = func(**args)
else:
result = func()
return str(result)
return self.__class__.__fakerReg.sub(_fakerRepl, old)
def _getSysStrValue(self, old: str, node: dict = None, fake: bool = True):
result = old
def _argSub(matched: re.Match):
value = node.get(matched.groups()[0])
return str(value)
result = self.__class__.__argsReg.sub(_argSub, result)
# 替换VARS
def _varSub(matched: re.Match):
return str(self.__vars.get(matched.groups()[0]))
result = self.__class__.__varsReg.sub(_varSub, result)
# 替换键盘Keys
def _keysSub(matched: re.Match):
return getattr(Keys, matched.groups()[0])
result = self.__class__.__keysReg.sub(_keysSub, result)
# 替换FAKER
if fake:
result = self._getFakeValue(result)
return result
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Python
1
https://gitee.com/RickyLi79/selenium-test-runner.git
git@gitee.com:RickyLi79/selenium-test-runner.git
RickyLi79
selenium-test-runner
SeleniumTestRunner
master

搜索帮助