├── data └── __init__.py ├── .DS_Store ├── log └── .DS_Store ├── aircases ├── .DS_Store ├── login │ └── login-out.air │ │ ├── tpl1681296909705.png │ │ └── login-out.py └── doctor │ └── doctor_list.air │ └── doctor_list.py ├── data.json ├── util └── config.py ├── README.md ├── requirements.txt ├── .gitignore ├── AirtestCase.py ├── run.py ├── report_tpl.html ├── report └── report_tpl.html └── report.html /data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeathKael/POM_UITest_Airtest/HEAD/.DS_Store -------------------------------------------------------------------------------- /log/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeathKael/POM_UITest_Airtest/HEAD/log/.DS_Store -------------------------------------------------------------------------------- /aircases/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeathKael/POM_UITest_Airtest/HEAD/aircases/.DS_Store -------------------------------------------------------------------------------- /aircases/login/login-out.air/tpl1681296909705.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeathKael/POM_UITest_Airtest/HEAD/aircases/login/login-out.air/tpl1681296909705.png -------------------------------------------------------------------------------- /data.json: -------------------------------------------------------------------------------- 1 | { 2 | "start": 1695370765.168117, 3 | "script": "mainlist.air", 4 | "tests": { 5 | "2fcaac91": { 6 | "status": 0, 7 | "path": "mainlist.air/log/2fcaac91/log.html" 8 | }, 9 | "9YE0218516000027": { 10 | "status": 0, 11 | "path": "mainlist.air/log/9YE0218516000027/log.html" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /util/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -* 2 | import os 3 | 4 | Androiddevice = ["Android://127.0.0.1:5037"] # 连接安卓设备127.0.0.1:5037固定写法172.16.81.115安卓真机的Ip 5 | airpath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'aircases') # 脚本目录 6 | logpath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'log') # 日志目录 7 | templatepath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'templates') # 模板目录 8 | reportpath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'report') # 报告目录 9 | datapath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'data') # 测试数据目录 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目介绍 2 | 1.项目基于airtest, 可将IDE编写好的脚本批量执行,批量连接设备执行,并产出测试报告 3 | ## 项目配置 4 | ### 安装requirements.txt的项目依赖 5 | ```cd airtest_uitest 6 | pip install -r requirements.txt 7 | ``` 8 | ### 运行项目 9 | ``` 10 | cd airtest_uitest 11 | // 批量执行脚本(单设备) 12 | python AirtestCase.py 13 | // 多设备执行脚本(单脚本多设备) 14 | python run.py 15 | ``` 16 | 17 | ### 项目结构 18 | 1. airtcases 用例文件.air .py 19 | a. doctor 20 | b. ... 21 | c. ... 22 | 2. data 数据文件 23 | 3. log 日志文件 24 | 4. report 报告文件 25 | 5. util 26 | a. config.py 配置文件 27 | 6. AirtestCase.py 多脚本批量执行脚本 28 | 7. run.py 多设备执行脚本 29 | 8. requirements.txt 依赖包目录 30 | 31 | ### air 脚本的开发 32 | 通过airtest工具,开发脚本,可以录制,也可以自己编写,请参考源文档 33 | https://airtest.doc.io.netease.com/ 34 | 35 | 36 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | airtest==1.3.0.1 2 | cached-property==1.5.2 3 | certifi==2023.5.7 4 | charset-normalizer==3.2.0 5 | colored==1.4.4 6 | decorator==5.1.1 7 | Deprecated==1.2.14 8 | deprecation==2.1.0 9 | facebook-wda==1.4.6 10 | ffmpeg-python==0.2.0 11 | filelock==3.12.2 12 | future==0.18.3 13 | hrpc==1.0.9 14 | idna==3.4 15 | Jinja2==3.1.2 16 | loguru==0.7.0 17 | logzero==1.7.0 18 | MarkupSafe==2.1.3 19 | mss==6.1.0 20 | numpy==1.21.6 21 | opencv-contrib-python==4.6.0.66 22 | packaging==23.1 23 | Pillow==9.5.0 24 | pocoui==1.0.89 25 | py==1.11.0 26 | python-xlib==0.33 27 | pywinauto==0.6.3 28 | requests==2.31.0 29 | retry==0.9.2 30 | simple-tornado==0.2.2 31 | simplejson==3.19.1 32 | six==1.16.0 33 | tabulate==0.9.0 34 | tidevice==0.11.0 35 | tornado==6.2 36 | urllib3==1.26.16 37 | websocket-client==0.48.0 38 | wrapt==1.15.0 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | dist/ 11 | develop-eggs/ 12 | .eggs/ 13 | *.egg-info/ 14 | .installed.cfg 15 | 16 | # PyInstaller 17 | # 通常用于打包Python应用,如果使用PyInstaller,可以添加以下规则 18 | build/ 19 | var/ 20 | *.spec 21 | 22 | # Jupyter Notebook files 23 | .ipynb_checkpoints/ 24 | 25 | # IPython 26 | profile_default/ 27 | ipython_config.py 28 | 29 | # pyenv 30 | .python-version 31 | 32 | # pipenv 33 | # 如果使用pipenv管理依赖,可以添加以下规则 34 | Pipfile.lock 35 | 36 | # virtualenv 37 | # 如果使用virtualenv,可以添加以下规则 38 | venv/ 39 | virtualenv/ 40 | .env/ 41 | 42 | # Spyder project settings 43 | .spyderproject 44 | 45 | # Rope project settings 46 | .ropeproject 47 | 48 | # mkdocs documentation 49 | /site 50 | 51 | # mypy 52 | # 如果使用mypy进行类型检查,可以添加以下规则 53 | .mypy_cache/ 54 | /.idea/ 55 | -------------------------------------------------------------------------------- /aircases/login/login-out.air/login-out.py: -------------------------------------------------------------------------------- 1 | # -*- encoding=utf8 -*- 2 | __author__ = "Administrator" 3 | 4 | from airtest.core.api import * 5 | 6 | auto_setup(__file__) 7 | 8 | 9 | 10 | from poco.drivers.android.uiautomation import AndroidUiautomationPoco 11 | poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False) 12 | 13 | # 打开crm 14 | start_app('com.igancao.crm') 15 | 16 | # 判断是否登录 17 | # 未登录 18 | if exists(Template(r"tpl1681296909705.png", record_pos=(0.255, -0.429), resolution=(1080, 2400))): 19 | # 点击验证码登录 20 | poco("com.igancao.crm:id/tvYan").click() 21 | # 输入账号 22 | poco("com.igancao.crm:id/etUsername").set_text('right#bocai') 23 | # 输入验证码 24 | poco("com.igancao.crm:id/etPassword").set_text('159357456') 25 | 26 | sleep(1.0) 27 | # 点击登录 28 | poco("com.igancao.crm:id/btnLogin").click() 29 | sleep(1.0) 30 | # assert_equal("实际值", "预测值", "请填写测试点.") 31 | 32 | # 已登录 33 | else: 34 | print('已登录') 35 | # 点击 我的 36 | poco("com.igancao.crm:id/rb4").click() 37 | 38 | # 点击 退出账户 39 | poco("com.igancao.crm:id/tvLogout").click() 40 | 41 | # 返回桌面 42 | keyevent("HOME") 43 | -------------------------------------------------------------------------------- /AirtestCase.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from airtest.cli.runner import AirtestCase, run_script 4 | from argparse import * 5 | import shutil 6 | from util.config import * 7 | # from lib.log import logger 8 | from airtest.report.report import simple_report 9 | 10 | 11 | class XFDAirtestCase(AirtestCase): # 继承AirtestCase类 12 | 13 | def setUp(self): 14 | # logger.info("案例开始执行") 15 | super(XFDAirtestCase, self).setUp() # 继承父类的setup方法 16 | 17 | def tearDown(self): 18 | # logger.info("案例执行结束") 19 | super(XFDAirtestCase, self).tearDown() # 继承父类的tearDown方法 20 | 21 | def run_air(self, root_dir, device): # 本方法主要是查找脚本文件,目录文件,初始化AirtestCase所需要的参数,执行脚本,并生成报告 22 | airlist = [] 23 | for i in range(len(os.listdir(root_dir))): 24 | if "." not in os.listdir(root_dir)[i]: 25 | airlist.append(root_dir+"/"+os.listdir(root_dir)[i]) 26 | 27 | for f in airlist: # 循环查找air所在的目录 28 | print(os.listdir(f)) 29 | for ff in os.listdir(f): 30 | print(ff) 31 | if ff.endswith(".air"): # 以air结尾的文件 32 | airName = ff 33 | script = os.path.join(f, ff) # 脚本目录 34 | # logger.info(script) 35 | log = os.path.join(logpath + '/' + airName.replace('.py', '')) # 日志目录 36 | print(log) 37 | # logger.info(log) 38 | if os.path.isdir(log): 39 | shutil.rmtree(log) # 清空日志目录文件 40 | else: 41 | os.makedirs(log) 42 | # 初始化父类AirtestCase所需要的参数 43 | args = Namespace(device=device, log=log, recording=None, script=script, compress=False, no_image=False) 44 | try: 45 | run_script(args, AirtestCase) # 执行air脚本文件 46 | except AssertionError: 47 | pass 48 | # logger.info("案例执行失败") 49 | finally: 50 | output_file = log + '/' + airName.replace('.py', '') + '.html' 51 | # logfile = log + '/' + 'log.txt' logfile=logfile, 52 | # output_file = logpath + '\\' + +airName.replace('.air', '') + '.html' # 生成报告目录 53 | simple_report(script, logpath=log, output=output_file) # 生成报告的方法 54 | # logger.info("案例执行成功") 55 | 56 | 57 | if __name__ == '__main__': 58 | test = XFDAirtestCase() 59 | # device = ['Android://127.0.0.1:5037/172.16.81.115:5555'] 60 | test.run_air(airpath, Androiddevice) 61 | -------------------------------------------------------------------------------- /aircases/doctor/doctor_list.air/doctor_list.py: -------------------------------------------------------------------------------- 1 | # -*- encoding=utf8 -*- 2 | __author__ = "Administrator" 3 | 4 | from airtest.core.api import * 5 | 6 | auto_setup(__file__) 7 | 8 | 9 | from poco.drivers.android.uiautomation import AndroidUiautomationPoco 10 | poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False) 11 | 12 | start_app('com.igancao.crm') 13 | sleep(1.0) 14 | 15 | # 判断是否登录 16 | # 未登录 17 | if poco("com.igancao.crm:id/tvYan"): 18 | # 点击验证码登录 19 | poco("com.igancao.crm:id/tvYan").click() 20 | # 输入账号 21 | poco("com.igancao.crm:id/etUsername").set_text('tester') 22 | # 输入验证码 23 | poco("com.igancao.crm:id/etPassword").set_text('password') 24 | 25 | sleep(1.0) 26 | # 点击登录 27 | poco("com.igancao.crm:id/btnLogin").click() 28 | sleep(1.0) 29 | # assert_equal("实际值", "预测值", "请填写测试点.") 30 | 31 | # 已登录 32 | else: 33 | print('已登录') 34 | sleep(3) 35 | # 点击底部医生tab 36 | poco('com.igancao.crm:id/rb2').click() 37 | 38 | assert_equal(poco('com.igancao.crm:id/tvTitle').get_text(), '我的医生', '是否进入我的医生页面') 39 | 40 | # 找到医生熊大 41 | doctor_name = '熊大' 42 | try: 43 | check_list =[] 44 | # 首页列表寻找医生:熊大 45 | while not poco(text=doctor_name): 46 | # 找到当前页面总条数 47 | items = poco("com.igancao.crm:id/tvName") 48 | nums = len(items) 49 | 50 | # 没找到则加载数据poco("") 51 | poco("com.igancao.crm:id/wmv").swipe([-0.025, -0.5]) 52 | 53 | 54 | latest_doctor_name = items[-1].get_text() 55 | 56 | check_list.append(latest_doctor_name) 57 | 58 | print(check_list) 59 | 60 | if len(check_list) >=2 and check_list[-1] == check_list[-2]: 61 | break 62 | 63 | # 找到医生后点击医生头像 64 | poco(text=doctor_name).click() 65 | sleep(1.0) 66 | 67 | # 断言医生姓名是否一致 68 | assert_equal(poco('com.igancao.crm:id/tvName').get_text(),doctor_name,'校验医生姓名是否一致') 69 | # 点击进入医生详情 70 | poco("com.igancao.crm:id/ivNext").click() 71 | # 断言医生详情内姓名展示是否一致 72 | assert_equal(poco('com.igancao.crm:id/tvName').get_text(), doctor_name, "校验医生详情姓名是否一致.") 73 | 74 | except: 75 | print("不存在医生:"+doctor_name) 76 | # 返回到我的医生页面 77 | while poco('com.igancao.crm:id/tvBack'): 78 | poco('com.igancao.crm:id/tvBack').click() 79 | 80 | assert_equal(poco('com.igancao.crm:id/tvTitle').get_text(), '我的医生', '是否进入我的医生页面') 81 | # 切换到代理医生 82 | poco("com.igancao.crm:id/tvTitle").click() 83 | poco("com.igancao.crm:id/tvProxyDoctors").click() 84 | assert_equal(poco('com.igancao.crm:id/tvTitle').get_text(), '代理医生', '是否进入代理医生列表') 85 | # 切换到区域医生 86 | poco("com.igancao.crm:id/tvTitle").click() 87 | poco("com.igancao.crm:id/tvAreaDoctor").click() 88 | assert_equal(poco('com.igancao.crm:id/tvTitle').get_text(), '区域医生', '是否进入区域医生列表') 89 | # 切换到团队医生 90 | poco("com.igancao.crm:id/tvTitle").click() 91 | poco("com.igancao.crm:id/tvGroup").click() 92 | assert_equal(poco('com.igancao.crm:id/tvTitle').get_text(), '团队医生', '是否进入团队医生列表') 93 | # 切换回我的医生列表 94 | poco("com.igancao.crm:id/tvTitle").click() 95 | poco("com.igancao.crm:id/tvMyDoctor").click() 96 | 97 | # 点击排序按钮 98 | poco("com.igancao.crm:id/ivRight").click() 99 | 100 | # 选择排序方式 101 | poco(text="注册时间从近到远").click() 102 | 103 | # 点击搜索按钮 104 | poco("com.igancao.crm:id/ivSearch").click() 105 | 106 | # 判断是否进入搜索页面 107 | assert_equal(poco("com.igancao.crm:id/etContent").get_text() 108 | , "请输入医生姓名、手机号、医院名称", "是否进入搜索页面") 109 | 110 | # 输入搜索关键词 111 | poco("com.igancao.crm:id/etContent").set_text('熊大') 112 | 113 | # 点击搜索按钮 114 | poco("com.igancao.crm:id/tvSure").click() 115 | 116 | # 搜索结果 117 | if poco("com.igancao.crm:id/tv"): 118 | poco("com.igancao.crm:id/tv").click() 119 | sleep(2) 120 | poco("com.igancao.crm:id/tvBack").click() 121 | 122 | 123 | stop_app('com.igancao.crm') 124 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | # -*- encoding=utf-8 -*- 2 | # Run Airtest in parallel on multi-device 3 | import os 4 | import traceback 5 | import subprocess 6 | import webbrowser 7 | import time 8 | import json 9 | import shutil 10 | from airtest.core.android.adb import ADB 11 | from jinja2 import Environment, FileSystemLoader 12 | 13 | 14 | def run(devices, air, run_all=False): 15 | """" 16 | run_all 17 | = True: 从头开始完整测试 18 | = False: 续着data.json的进度继续测试 19 | """ 20 | try: 21 | results = load_jdon_data(air, run_all) 22 | tasks = run_on_multi_device(devices, air, results, run_all) 23 | for task in tasks: 24 | status = task['process'].wait() 25 | results['tests'][task['dev']] = run_one_report(task['air'], task['dev']) 26 | results['tests'][task['dev']]['status'] = status 27 | json.dump(results, open('data.json', "w"), indent=4) 28 | run_summary(results) 29 | except Exception as e: 30 | traceback.print_exc(e) 31 | 32 | 33 | def run_on_multi_device(devices, air, results, run_all): 34 | """ 35 | 在多台设备上运行airtest脚本 36 | """ 37 | tasks = [] 38 | for dev in devices: 39 | if (not run_all and results['tests'].get(dev) and 40 | results['tests'].get(dev).get('status') == 0): 41 | print("Skip device %s" % dev) 42 | continue 43 | log_dir = get_log_dir(dev, air) 44 | cmd = [ 45 | "airtest", 46 | "run", 47 | air, 48 | "--device", 49 | "Android:///" + dev, 50 | "--log", 51 | log_dir, 52 | ] 53 | try: 54 | tasks.append({ 55 | 'process': subprocess.Popen(cmd, cwd=os.getcwd()), 56 | 'dev': dev, 57 | 'air': air 58 | }) 59 | except Exception as e: 60 | traceback.print_exc() 61 | return tasks 62 | 63 | 64 | def run_one_report(air, dev): 65 | """" 66 | 生成一个脚本的测试报告 67 | """ 68 | try: 69 | log_dir = get_log_dir(dev, air) 70 | log = os.path.join(log_dir, 'log.txt') 71 | if os.path.isfile(log): 72 | cmd = [ 73 | "airtest", 74 | "report", 75 | air, 76 | "--log_root", 77 | "./"+log_dir, 78 | "--outfile", 79 | "./"+os.path.join(log_dir, 'log.html'), 80 | "--lang", 81 | "zh" 82 | ] 83 | ret = subprocess.call(cmd, cwd=os.getcwd()) 84 | return { 85 | 'status': ret, 86 | 'path': os.path.join(log_dir, 'log.html') 87 | } 88 | else: 89 | print("Report build Failed. File not found in dir %s" % log) 90 | except Exception as e: 91 | traceback.print_exc(e) 92 | return {'status': -1, 'device': dev, 'path': '33333'} 93 | 94 | 95 | def run_summary(data): 96 | """" 97 | 生成汇总的测试报告 98 | """ 99 | try: 100 | summary = { 101 | 'time': "%.3f" % (time.time() - data['start']), 102 | 'success': [item['status'] for item in data['tests'].values()].count(0), 103 | 'count': len(data['tests']) 104 | } 105 | summary.update(data) 106 | summary['start'] = time.strftime("%Y-%m-%d %H:%M:%S", 107 | time.localtime(data['start'])) 108 | env = Environment(loader=FileSystemLoader(os.getcwd()), 109 | trim_blocks=True) 110 | html = env.get_template('report_tpl.html').render(data=summary) 111 | with open("report.html", "w", encoding="utf-8") as f: 112 | f.write(html) 113 | webbrowser.open('report.html') 114 | except Exception as e: 115 | traceback.print_exc() 116 | 117 | 118 | def load_jdon_data(air, run_all): 119 | """" 120 | 加载进度 121 | 如果data.json存在且run_all=False,加载进度 122 | 否则,返回一个空的进度数据 123 | """ 124 | json_file = os.path.join(os.getcwd(), 'data.json') 125 | if (not run_all) and os.path.isfile(json_file): 126 | data = json.load(open(json_file)) 127 | data['start'] = time.time() 128 | return data 129 | else: 130 | clear_log_dir(air) 131 | return { 132 | 'start': time.time(), 133 | 'script': air, 134 | 'tests': {} 135 | 136 | } 137 | 138 | 139 | def clear_log_dir(air): 140 | """" 141 | 清理log文件夹 142 | """ 143 | log = os.path.join(os.getcwd(), air, 'log') 144 | if os.path.exists(log): 145 | shutil.rmtree(log) 146 | 147 | 148 | def get_log_dir(device, air): 149 | """" 150 | 在 /log/ 文件夹下创建每台设备的运行日志文件夹 151 | """ 152 | log_dir = os.path.join(air, 'log', device.replace(".", "_").replace(':', '_')) 153 | if not os.path.exists(log_dir): 154 | os.makedirs(log_dir) 155 | return log_dir 156 | 157 | 158 | if __name__ == '__main__': 159 | """ 160 | 初始化数据 161 | """ 162 | devices = [tmp[0] for tmp in ADB().devices()] 163 | air = 'aircases/doctor/doctor_list.air' 164 | 165 | # Continue tests saved in data.json 166 | # Skip scripts that run succeed 167 | # 基于data.json的进度,跳过已运行成功的脚本 168 | # run(devices, air) 169 | 170 | # Resun all script 171 | # 重新运行所有脚本 172 | run(devices, air, run_all=True) 173 | -------------------------------------------------------------------------------- /report_tpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Airtest 多设备并行测试结果汇总 9 | 10 | 203 | 204 |
205 |
206 |
207 |
208 |

{{data['script']}}

209 |
210 |
Switch to English version
211 |
开始时间:{{data['start']}},耗时 {{data['time']}}
212 |
Started at:{{data['start']}},cost {{data['time']}} s
213 |
214 |
215 |
成功率: {{data["success"]}}/{{data["count"]}}
216 |
Ruccess rate: {{data["success"]}}/{{data["count"]}}
217 |
218 |
219 |
220 | {{'%0.2f' % (data["success"] *100 / data["count"])}}% 221 |
222 |
223 |
224 |
225 | 226 |
227 |
228 | 运行详情 229 | Detail 230 |
231 |
232 |
233 |
序号
234 |
状态
235 |
设备
236 |
id
237 |
result
238 |
device
239 |
--
240 |
241 | {% for dev, item in data['tests'].items() %} 242 |
243 |
{{loop.index}}
244 |
{{"成功" if item['status']==0 else "失败"}}
245 |
{{"sucess" if item['status']==0 else "failed"}}
246 |
{{dev}}
247 |
点击可查看详情
248 |
click to see detail
249 |
250 | {% endfor %} 251 |
252 |
253 |
254 |
255 |
256 |
257 | 258 |
259 |
260 | 261 |
262 |
263 |
264 | 265 | 354 | 355 | 356 | 357 | 358 | -------------------------------------------------------------------------------- /report/report_tpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Airtest 多设备并行测试结果汇总 9 | 10 | 203 | 204 |
205 |
206 |
207 |
208 |

{{data['script']}}

209 |
210 |
Switch to English version
211 |
开始时间:{{data['start']}},耗时 {{data['time']}}
212 |
Started at:{{data['start']}},cost {{data['time']}} s
213 |
214 |
215 |
成功率: {{data["success"]}}/{{data["count"]}}
216 |
Ruccess rate: {{data["success"]}}/{{data["count"]}}
217 |
218 |
219 |
220 | {{'%0.2f' % (data["success"] *100 / data["count"])}}% 221 |
222 |
223 |
224 |
225 | 226 |
227 |
228 | 运行详情 229 | Detail 230 |
231 |
232 |
233 |
序号
234 |
状态
235 |
设备
236 |
id
237 |
result
238 |
device
239 |
--
240 |
241 | {% for dev, item in data['tests'].items() %} 242 |
243 |
{{loop.index}}
244 |
{{"成功" if item['status']==0 else "失败"}}
245 |
{{"sucess" if item['status']==0 else "failed"}}
246 |
{{dev}}
247 |
点击可查看详情
248 |
click to see detail
249 |
250 | {% endfor %} 251 |
252 |
253 |
254 |
255 |
256 |
257 | 258 |
259 |
260 | 261 |
262 |
263 |
264 | 265 | 354 | 355 | 356 | 357 | 358 | -------------------------------------------------------------------------------- /report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Airtest 多设备并行测试结果汇总 9 | 10 | 203 | 204 |
205 |
206 |
207 |
208 |

mainlist.air

209 |
210 |
Switch to English version
211 |
开始时间:2023-09-22 16:19:25,耗时 108.310
212 |
Started at:2023-09-22 16:19:25,cost 108.310 s
213 |
214 |
215 |
成功率: 2/2
216 |
Ruccess rate: 2/2
217 |
218 |
219 |
220 | 100.00% 221 |
222 |
223 |
224 |
225 | 226 |
227 |
228 | 运行详情 229 | Detail 230 |
231 |
232 |
233 |
序号
234 |
状态
235 |
设备
236 |
id
237 |
result
238 |
device
239 |
--
240 |
241 |
242 |
1
243 |
成功
244 |
sucess
245 |
2fcaac91
246 |
点击可查看详情
247 |
click to see detail
248 |
249 |
250 |
2
251 |
成功
252 |
sucess
253 |
9YE0218516000027
254 |
点击可查看详情
255 |
click to see detail
256 |
257 |
258 |
2
259 |
成功
260 |
sucess
261 |
9YE0218516000027
262 |
点击可查看详情
263 |
click to see detail
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 | 272 |
273 |
274 | 275 |
276 |
277 |
278 | 279 | 368 | 369 | 370 | 371 | --------------------------------------------------------------------------------