├── python.exe ├── screenshot ├── 领取京豆.jpg ├── 京东金融签到.jpg ├── 掌上生活签到.jpg ├── 京东金融提额包.jpg ├── 网易云音乐签到.jpg └── 联通营业厅签到.jpg ├── __pycache__ ├── schedule.cpython-35.pyc ├── test_apps.cpython-35.pyc ├── get_device.cpython-35.pyc ├── HTMLTestRunner.cpython-35.pyc ├── appium_server.cpython-35.pyc └── appium_service.cpython-35.pyc ├── public ├── __pycache__ │ ├── get_device.cpython-35.pyc │ ├── schedule.cpython-35.pyc │ └── appium_server.cpython-35.pyc ├── get_device.py └── schedule.py ├── start_appium_server.bat ├── stop_appium_server.bat ├── .idea ├── vcs.xml ├── misc.xml ├── modules.xml ├── daily_task.iml └── workspace.xml ├── appium_server.py ├── README.md ├── run.py ├── report └── 2017-12-12 result.html ├── test_apps.py └── HTMLTestRunner.py /python.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/python.exe -------------------------------------------------------------------------------- /screenshot/领取京豆.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/screenshot/领取京豆.jpg -------------------------------------------------------------------------------- /screenshot/京东金融签到.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/screenshot/京东金融签到.jpg -------------------------------------------------------------------------------- /screenshot/掌上生活签到.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/screenshot/掌上生活签到.jpg -------------------------------------------------------------------------------- /screenshot/京东金融提额包.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/screenshot/京东金融提额包.jpg -------------------------------------------------------------------------------- /screenshot/网易云音乐签到.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/screenshot/网易云音乐签到.jpg -------------------------------------------------------------------------------- /screenshot/联通营业厅签到.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/screenshot/联通营业厅签到.jpg -------------------------------------------------------------------------------- /__pycache__/schedule.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/__pycache__/schedule.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/test_apps.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/__pycache__/test_apps.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/get_device.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/__pycache__/get_device.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/HTMLTestRunner.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/__pycache__/HTMLTestRunner.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/appium_server.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/__pycache__/appium_server.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/appium_service.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/__pycache__/appium_service.cpython-35.pyc -------------------------------------------------------------------------------- /public/__pycache__/get_device.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/public/__pycache__/get_device.cpython-35.pyc -------------------------------------------------------------------------------- /public/__pycache__/schedule.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/public/__pycache__/schedule.cpython-35.pyc -------------------------------------------------------------------------------- /public/__pycache__/appium_server.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtfghd/AppTask/HEAD/public/__pycache__/appium_server.cpython-35.pyc -------------------------------------------------------------------------------- /start_appium_server.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | title start_appium_server 3 | cmd /c "appium -a 127.0.0.1 -p 4723 -bp 4728 --chromedriver-port 9519 -U emulator-5554" -------------------------------------------------------------------------------- /stop_appium_server.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | title stop_appium_server 3 | tasklist -v | find "start_appium_server">nul 4 | if %errorlevel%==0 ( 5 | taskkill -fi "WINDOWTITLE eq start_appium_server" 6 | ) -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /appium_server.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | import os,time 3 | 4 | def start_appium_server(): 5 | '''启动appium服务''' 6 | os.system('start start_appium_server.bat') 7 | print('appium服务启动成功') 8 | 9 | def stop_appium_server(): 10 | '''关闭appium服务''' 11 | os.system('start stop_appium_server.bat') 12 | print('appium服务已关闭') 13 | -------------------------------------------------------------------------------- /public/get_device.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | import os,time 3 | 4 | def start_android_devices(): 5 | '''启动安卓模拟器''' 6 | command = r'start D:\Application\dnplayer2\dnplayer.exe' 7 | os.system(command) 8 | print('模拟器启动成功') 9 | print('\n') 10 | 11 | def stop_android_devices(): 12 | '''结束安卓模拟器进程''' 13 | command = r'taskkill -f -im dnplayer.exe' 14 | os.system(command) 15 | print('所有任务执行完毕,关闭模拟器') 16 | print('\n') 17 | -------------------------------------------------------------------------------- /public/schedule.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | 3 | done = [ 4 | '京东金融签到领金币', 5 | '京东金融领取每周提额包', 6 | '京东APP签到领京豆', 7 | '网易云音乐签到', 8 | '联通营业厅签到', 9 | '掌上生活签到领积分', 10 | '尤果网APP' 11 | ] 12 | 13 | in_process = [ 14 | 'IT之家每日金币和经验', 15 | '飞猪每日签到领流量' 16 | ] 17 | 18 | def process(): 19 | print('=' * 10 + '打造自己的任务自动处理池' + '=' * 10) 20 | print('已经完成的任务:') 21 | for i in done: 22 | print('\t'+i) 23 | 24 | print('\n'+'正在开发的任务:') 25 | for i in in_process: 26 | print('\t'+i) 27 | 28 | print('=' * 43) 29 | -------------------------------------------------------------------------------- /.idea/daily_task.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目背景 2 | 如今手机里面现在越来越多的APP需要每天签到,点赞,评论等任务,本来想的是用接口自动化来实现,操作的时候发现很多APP的接口(尤其金融类)比较麻烦,userkey啊token啊什么的都是加密且经常变化的,并且接口之间的关联耦合不太容易整理,一个个找接口去签到的话比较费时费力。所以转而用UI自动化来实现,python+appium+unittest写好框架,以后有新的APP只要用几分钟时间把这个APP的任务添加一下就好了。 3 | 4 | 5 | # 环境准备 6 | 以下是我使用时候的版本,根据自己情况安装就行
7 | JDK (1.8.0_91)
8 | SDK (25.1.7)
9 | python 3
10 | node.js (v6.11.0)
11 | appium server (1.4.16)
12 | Appium-Python-Client (0.24)
13 | 雷电安卓模拟器(安卓5.1.1,720*1280) 14 | 15 | 16 | # 目录概览 17 | ``` 18 | │ run.py 19 | │ appium_server.py 20 | │ test_apps.py 21 | │ HTMLTestRunner.py 22 | │ python.exe 23 | │ README.md 24 | │ start_appium_server.bat 25 | │ stop_appium_server.bat 26 | ├─public 27 | │ │ get_device.py 28 | │ │ schedule.py 29 | ├─report 30 | │ 2017-12-12 result.html 31 | ├─screenshot 32 | │ 京东金融签到.jpg 33 | │ 网易云音乐签到.jpg 34 | ``` 35 | 36 | - `run.py` 启动整个项目就运行这个文件 37 | - `appium_server.py` 里面有两个函数,执行启动/关闭appium服务批处理文件的命令 38 | - `test_apps.py` 所有的APP任务在这个文件,每个APP是一个函数,都以test开头 39 | - `HTMLTestRunner.py` 用来生成html测试报告 40 | - `start_appium_server.bat` 启动appium服务的批处理文件 41 | - `stop_appium_server.bat` 关闭appium服务的批处理文件 42 | - [ public ] 放一些执行每个任务都会用到的公共文件,包括启动/关闭安卓模拟器脚本,以及项目进度说明。 43 | - [ report ] 任务执行结果以html格式的报告放在在这个目录 44 | - [ screenshot ] 执行APP任务时的截图放在目录 45 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | import unittest,time,os 3 | from HTMLTestRunner import HTMLTestRunner 4 | from test_apps import * 5 | from public.get_device import * 6 | from public.schedule import * 7 | from appium_server import * 8 | import smtplib 9 | from email.mime.text import MIMEText 10 | from email.header import Header 11 | from email.mime.multipart import MIMEMultipart 12 | 13 | test_path = 'E:/daily_task_2' 14 | report_path = 'E:/daily_task_2/report/' 15 | 16 | def run_tasks(): 17 | '''执行所有APP任务''' 18 | discover = unittest.defaultTestLoader.discover(test_path, pattern='test_*.py') 19 | now = time.strftime('%Y-%m-%d') 20 | filename = report_path + now + ' result.html' # 这个filename是生成的自动化测试报告的文件名 21 | fp = open(filename, 'wb') 22 | runner = HTMLTestRunner(stream=fp, title='测试报告', description='用例的执行情况') 23 | runner.run(discover) 24 | fp.close() 25 | 26 | 27 | def send_mail(): 28 | '''任务完成后发送邮件通知''' 29 | # ----------找到当天的执行报告---------- 30 | lists = os.listdir(report_path) 31 | for i in lists: 32 | now = time.strftime('%Y-%m-%d') 33 | if now in i: 34 | AppTaskReport = os.path.join(report_path, i) # 获取html格式的测试报告 35 | else: 36 | pass 37 | 38 | # ----------创建一个带附件的实例---------- 39 | msg = MIMEMultipart() 40 | msg['Subject'] = Header('AppDailyTask', 'utf-8') 41 | msg['From'] = Header('peili', 'utf-8') 42 | msg['To'] = Header('peili', 'utf-8') 43 | 44 | # 添加邮件正文内容 45 | main_body_info = ''' 46 | 47 |

附件是每日任务执行报告

48 |

绿色代表该APP任务执行成功并断言正确

49 |

橙色代表该APP任务执行成功但断言错误,请查看截图文件或者打开APP确认任务是否完成

50 |

红色代表任务执行失败,可能是APP有更新,页面突然有活动/广告弹窗或者网络问题导致,请检查网络和APP

51 | ''' 52 | msg.attach(MIMEText(main_body_info, 'html', 'utf-8')) 53 | 54 | # 添加附件 55 | file1 = MIMEText(open(AppTaskReport, 'rb').read(), 'plain', 'utf-8') 56 | file1['Content-Type'] = 'application/octet-stream' 57 | file1["Content-Disposition"] = 'attachment; filename="AppTaskReport.html"' # 这里的filename就是邮件中附件的名字,可以自己命名 58 | msg.attach(file1) 59 | 60 | 61 | # ----------登录并发送---------- 62 | try: 63 | smtp = smtplib.SMTP_SSL('smtp.qq.com', 465) # QQ邮箱发送服务器以及端口,SMTP默认端口是25,这里改成465 64 | smtp.login('123456789@qq.com', '********') # 如果是QQ邮箱的话,第二个参数不是直接用的密码,用的是QQ邮箱的授权码 65 | smtp.sendmail('123456789@qq.com', '123456789@qq.com', msg.as_string()) # 前两个参数分别是发送邮箱和接收邮箱 66 | smtp.quit() 67 | print('邮件发送成功') 68 | except smtplib.SMTPException as e: 69 | print(e) 70 | 71 | 72 | if __name__ == '__main__': 73 | 74 | process() #打印当前开发进度 75 | start_appium_server() #启动appium服务 76 | start_android_devices() #启动模拟器 77 | time.sleep(15) 78 | run_tasks() #执行APP任务 79 | send_mail() #发送邮件 80 | stop_android_devices() #关闭模拟器 81 | stop_appium_server() # 关闭appium服务 82 | -------------------------------------------------------------------------------- /report/2017-12-12 result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 测试报告 6 | 7 | 8 | 9 | 92 | 93 | 94 | 95 | 189 | 190 |
191 |

测试报告

192 |

Start Time: 2017-12-12 10:43:40

193 |

Duration: 0:00:56.222428

194 |

Status: Failure 1

195 | 196 |

用例的执行情况

197 |
198 | 199 | 200 | 201 |

Show 202 | Summary 203 | Failed 204 | All 205 |

206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 |
Test Group/Test caseCountPassFailErrorView
test_apps.AppTask1010Detail
test_005_zssh: 掌上生活每日签到
236 | 237 | 238 | 239 | fail 240 | 241 | 259 | 260 | 261 |
Total1010 
273 | 274 |
 
275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /test_apps.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | from appium import webdriver 3 | import time,random,unittest 4 | from appium.webdriver.common.touch_action import TouchAction 5 | from HTMLTestRunner import HTMLTestRunner 6 | 7 | 8 | screenshot_path = 'E:/daily_task_2/screenshot/' 9 | 10 | class AppTask(unittest.TestCase): 11 | 12 | def basic(package_name,activity_name): 13 | '''启动应用''' 14 | global driver 15 | desired_caps = {} 16 | desired_caps['platformName'] = 'Android' 17 | desired_caps['platformVersion'] = '5.1' 18 | desired_caps['deviceName'] = 'emulator-5554' 19 | desired_caps['appPackage'] = package_name 20 | desired_caps['appActivity'] = activity_name 21 | desired_caps["unicodeKeyboard"] = "True" 22 | desired_caps["resetKeyboard"] = "True" 23 | driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) 24 | time.sleep(15) 25 | 26 | def tearDown(self): 27 | '''关闭应用''' 28 | driver.quit() 29 | 30 | def test_001_jd_finance(self): 31 | '''京东金融签到/领取提额包''' 32 | #----------启动应用---------- 33 | AppTask.basic('com.jd.jrapp','.WelcomeActivity') 34 | 35 | #----------九宫格滑动解锁---------- 36 | TouchAction(driver).press(x=180, y=598).move_to(x=0, y=0).wait(100).move_to(x=0, y=181).wait(100).move_to(x=0, y=181).wait(100).move_to(x=181, y=0).wait(100).move_to(x=181,y=0).release().perform() 37 | time.sleep(2) 38 | #这里重点注意,x,y的值是偏移量,不是坐标,参考https://testerhome.com/topics/9698 39 | 40 | # ----------检验是否有更新---------- 41 | update = driver.page_source.find('跳过') #判断是否有更新按钮 42 | if update != -1: 43 | driver.find_element_by_id('com.jd.jrapp:id/cancel').click() #点击"跳过",不更新 44 | time.sleep(1) 45 | else: 46 | pass 47 | 48 | #----------领取白条提额包---------- 49 | cancel1 = driver.page_source.find('com.jd.jrapp:id/ibtn_zc_product_notice_board_close') #判断进入首页时是否有消息弹窗 50 | if cancel1 != -1: 51 | driver.find_element_by_id('com.jd.jrapp:id/ibtn_zc_product_notice_board_close').click() # 点击关闭弹窗 52 | print('进入首页时的弹窗已关闭') 53 | driver.find_elements_by_id('com.jd.jrapp:id/tv_tab_strip')[1].click() #点击顶部白条按钮 54 | time.sleep(1) 55 | #这里再做一次弹窗的判断,使用过程中发现这里也有弹窗 56 | cancel1 = driver.page_source.find('com.jd.jrapp:id/ibtn_zc_product_notice_board_close') 57 | if cancel1 == -1: 58 | driver.find_element_by_id('com.jd.jrapp:id/tv_mid_text').click() #点击白条资产卡片 59 | time.sleep(1) 60 | else: 61 | driver.find_element_by_id('com.jd.jrapp:id/ibtn_zc_product_notice_board_close').click() # 点击关闭弹窗 62 | driver.find_element_by_id('com.jd.jrapp:id/tv_mid_text').click() # 点击白条资产卡片 63 | time.sleep(1) 64 | else: 65 | driver.find_elements_by_id('com.jd.jrapp:id/tv_tab_strip')[1].click() # 点击顶部白条按钮 66 | time.sleep(1) 67 | cancel1 = driver.page_source.find('com.jd.jrapp:id/ibtn_zc_product_notice_board_close') 68 | if cancel1 == -1: 69 | driver.find_element_by_id('com.jd.jrapp:id/tv_mid_text').click() #点击白条资产卡片 70 | time.sleep(3) 71 | else: 72 | driver.find_element_by_id('com.jd.jrapp:id/ibtn_zc_product_notice_board_close').click() # 点击关闭弹窗 73 | driver.find_element_by_id('com.jd.jrapp:id/tv_mid_text').click() # 点击白条资产卡片 74 | time.sleep(3) 75 | 76 | ti_button = driver.page_source.find('1个提额包') #判断是否有"1个提额包"的按钮 77 | if ti_button != -1: #如果不等于-1则证明提额的按钮存在,否则这里是"额度管理"的按钮 78 | time.sleep(1) 79 | driver.find_element_by_id('com.jd.jrapp:id/tv_right_oval_tips').click() #点击"1个提额包"按钮 80 | time.sleep(10) 81 | driver.swipe(640,430,640,430,10) #点击"提额"按钮 82 | time.sleep(1) 83 | driver.get_screenshot_as_file(screenshot_path + '京东金融提额包.jpg') #提额完成后截图保存 84 | print('提额完成') 85 | driver.find_element_by_id('com.jd.jrapp:id/btn_left').click() #点击左上角的"X"返回上一步 86 | driver.keyevent(4) # appium模拟手机按钮的方法,这里是模拟返回键继续返回上一步,详见:http://www.cnblogs.com/jiuyigirl/p/7126753.html 87 | else: 88 | time.sleep(3) 89 | driver.get_screenshot_as_file(screenshot_path + '京东金融提额包.jpg') # 提额完成后截图保存 90 | print('本周已经领取过提额包,下周再来') 91 | pass 92 | driver.keyevent(4) 93 | time.sleep(1) 94 | 95 | #----------个人中心签到---------- 96 | driver.find_element_by_id('com.jd.jrapp:id/fourthLayout').click() #点击个人中心 97 | time.sleep(1) 98 | driver.find_elements_by_id('com.jd.jrapp:id/tv_item_label')[0].click() #获取这一类标签,签到是第一个,并点击签到 99 | time.sleep(20) 100 | 101 | driver.swipe(600, 410, 600, 410, 10) # 点击"签到领钢镚"按钮 102 | time.sleep(1) 103 | driver.get_screenshot_as_file(screenshot_path + '京东金融签到.jpg') # 签到完成后截图保存 104 | driver.find_element_by_id('com.jd.jrapp:id/common_webview_navbar_left').click() 105 | time.sleep(1) 106 | print('今日签到完成') 107 | self.assertIn('已签', driver.page_source,msg='任务有失败,请到截图目录查看截图'+str(screenshot_path)) 108 | 109 | 110 | def test_002_jd_app(self): 111 | '''京东APP签到领京豆''' 112 | # ----------启动应用---------- 113 | AppTask.basic('com.jingdong.app.mall', '.main.MainActivity') 114 | 115 | # ----------点击主页"领京豆"---------- 116 | driver.find_element_by_xpath('//android.widget.RelativeLayout[contains(@index,6)]').click() #点击主页"领京豆"按钮 117 | time.sleep(15) 118 | ad_window = driver.page_source.find('1. 10月31日-11月11日,淘气的豆豆会在领京豆频道出没,找到它就能获得京豆或优惠券奖励!') 119 | if ad_window != -1: #判断签到页面是否有活动弹窗 120 | # driver.find_element_by_xpath('//android.view.View[contains(@index,0)]').click() 121 | driver.swipe(615,310,615,310) #关闭弹窗 122 | time.sleep(1) 123 | check_in = driver.page_source.find('签到领京豆')#判断是否已经领取过 124 | if check_in != -1: 125 | driver.find_element_by_xpath('//android.widget.TextView[contains(@text,"签到领京豆")]').click() #点击签到 126 | time.sleep(2) 127 | driver.swipe(360, 600, 360, 600, 10) # 点击翻牌 128 | time.sleep(2) 129 | print('今日签到领取京豆完成') 130 | driver.get_screenshot_as_file(screenshot_path + '领取京豆.jpg') 131 | else: 132 | print('今天已经领取过京豆,明天再来') 133 | time.sleep(2) 134 | driver.get_screenshot_as_file(screenshot_path + '领取京豆.jpg') 135 | else: 136 | check_in = driver.page_source.find('签到领京豆') # 判断是否已经领取过 137 | if check_in != -1: 138 | driver.find_element_by_xpath('//android.widget.TextView[contains(@text,"签到领京豆")]').click() # 点击签到 139 | time.sleep(2) 140 | driver.swipe(360, 600, 360, 600, 10) # 点击翻牌 141 | time.sleep(2) 142 | print('今日签到领取京豆完成') 143 | driver.get_screenshot_as_file(screenshot_path + '领取京豆.jpg') 144 | else: 145 | print('今天已经领取过京豆,明天再来') 146 | time.sleep(2) 147 | driver.get_screenshot_as_file(screenshot_path + '领取京豆.jpg') 148 | 149 | self.assertIn('今日已签到,已翻牌', driver.page_source,msg='任务有失败,请到截图目录查看截图'+str(screenshot_path)) 150 | 151 | 152 | def test_003_wyy_music(self): 153 | '''网易云音乐每日签到''' 154 | # ----------启动应用---------- 155 | AppTask.basic('com.netease.cloudmusic', '.activity.MainActivity') 156 | 157 | # ----------左侧签到---------- 158 | driver.find_element_by_id('com.netease.cloudmusic:id/ma').click() #点击主页左上角的汉堡菜单 159 | check_in = driver.page_source.find('已签到') 160 | if check_in != -1: 161 | driver.find_element_by_id('com.netease.cloudmusic:id/a35').click() # 点击个人中心的"签到"按钮 162 | time.sleep(5) 163 | driver.keyevent(4) # 按手机回退建 164 | time.sleep(1) 165 | driver.find_element_by_id('com.netease.cloudmusic:id/ma').click() # 点击主页左上角的汉堡菜单 166 | driver.get_screenshot_as_file(screenshot_path + '网易云音乐签到.jpg') 167 | else: 168 | print('今日已经签到,明天再来') 169 | driver.get_screenshot_as_file(screenshot_path + '网易云音乐签到.jpg') 170 | 171 | self.assertIn('已签到', driver.page_source,msg='任务有失败,请到截图目录查看截图'+str(screenshot_path)) 172 | 173 | 174 | def test_004_lt_yingyeting(self): 175 | '''联通手机营业厅每日签到''' 176 | # ----------启动应用---------- 177 | AppTask.basic('com.sinovatech.unicom.ui', 'com.sinovatech.unicom.basic.ui.MainActivity') 178 | 179 | # ----------检验是否有更新---------- 180 | update = driver.page_source.find('不,谢谢') #判断是否有更新按钮 181 | if update != -1: 182 | driver.find_element_by_id('com.sinovatech.unicom.ui:id/custom_dialog_cancel_button').click() #点击"跳过",不更新 183 | time.sleep(1) 184 | else: 185 | pass 186 | 187 | # ----------点击签到--------- 188 | driver.find_element_by_id('com.sinovatech.unicom.ui:id/home_header_long_qiandao_image').click() #左上角签到LOGO 189 | time.sleep(8) 190 | 191 | check_in = driver.page_source.find('已签到') 192 | if check_in == -1: 193 | driver.swipe(600,260,600,260,10) #点击“签到” 194 | time.sleep(1) 195 | driver.get_screenshot_as_file(screenshot_path + '联通营业厅签到.jpg') # 签到完成后截图保存 196 | print('签到完成') 197 | else: 198 | driver.get_screenshot_as_file(screenshot_path + '联通营业厅签到.jpg') 199 | print('签到完成') 200 | 201 | self.assertIn('已签到', driver.page_source, msg='任务有失败,请到截图目录查看截图') 202 | 203 | 204 | def test_005_zssh(self): 205 | '''掌上生活每日签到''' 206 | # ----------启动应用---------- 207 | AppTask.basic('com.cmbchina.ccd.pluto.cmbActivity', '.SplashActivity') 208 | 209 | # ----------执行签到---------- 210 | driver.find_element_by_id('com.cmbchina.ccd.pluto.cmbActivity:id/btn_fourth_menu').click() #点击个人中心 211 | time.sleep(1) 212 | driver.find_element_by_xpath('//android.widget.TextView[contains(@index,0)]').click() #点击顶部登陆按钮 213 | time.sleep(3) 214 | 215 | #----------九宫格滑动解锁---------- 216 | TouchAction(driver).press(x=140, y=586).move_to(x=0, y=0).wait(100).move_to(x=0, y=222).wait(100).move_to(x=0, y=222).wait(100).move_to(x=222, y=0).wait(100).move_to(x=222,y=0).release().perform() 217 | time.sleep(3) 218 | 219 | driver.find_element_by_xpath('//android.widget.ImageView[contains(@index,2)]').click() #点击"签到有礼"按钮 220 | time.sleep(5) 221 | driver.swipe(355,490,355,490,10) #点击签到卡片页 222 | time.sleep(2) 223 | driver.find_element_by_id('com.cmbchina.ccd.pluto.cmbActivity:id/btn_get').click() #点击"每日签到1积分" 224 | time.sleep(8) 225 | 226 | check_in = driver.page_source.find('立即领取') 227 | if check_in != -1: 228 | driver.find_element_by_id('com.cmbchina.ccd.pluto.cmbActivity:id/btn_winner_unentity_order_pay').click() #点击"立即领取"按钮 229 | time.sleep(5) 230 | driver.get_screenshot_as_file(screenshot_path + '掌上生活签到.jpg') # 签到完成后截图保存 231 | print('签到完成') 232 | else: 233 | time.sleep(1) 234 | driver.get_screenshot_as_file(screenshot_path + '掌上生活签到.jpg') # 签到完成后截图保存 235 | print('签到完成') 236 | 237 | self.assertIn('领取成功', driver.page_source, msg='任务有失败,请到截图目录查看截图') 238 | 239 | 240 | def atest_006_youguo(self): 241 | '''尤果圈每日任务''' 242 | # ----------启动应用---------- 243 | AppTask.basic('com.ugirls.app02', '.module.splash.SplashActivity') 244 | 245 | # ----------每日签到---------- 246 | driver.find_element_by_id('com.ugirls.app02:id/spread_layout').click() #点击主页"签到"按钮 247 | time.sleep(2) 248 | driver.find_element_by_id('com.ugirls.app02:id/signin_bt').click() #点击"今日签到"按钮 249 | time.sleep(2) 250 | driver.keyevent(4) #按手机回退建 251 | 252 | # ----------分享链接---------- 253 | driver.find_element_by_id('com.ugirls.app02:id/ibt_photo').click() # 点击主页下方图片模块 254 | time.sleep(3) 255 | for i in range(2): 256 | driver.find_elements_by_id('com.ugirls.app02:id/more')[0].click() # 点击第一个卡片右下角的更多选项 257 | time.sleep(1) 258 | driver.find_element_by_id('com.ugirls.app02:id/qq')[0].click() # 分享给QQ好友 259 | time.sleep(1) 260 | driver.swipe(500,500,500,500,10) #发送给最近聊天的第一个人 261 | time.sleep(1) 262 | driver.swipe(500,850,500,850,10) #点击发送 263 | time.sleep(1) 264 | driver.swipe(100, 700, 100, 700, 10) # 点击返回尤果圈 265 | 266 | # ----------点赞评论---------- 267 | for i in range(6): 268 | driver.swipe(200, 1050, 200, 460, 100) #每次上划点击左上角第一张图片 269 | driver.swipe(200,460,200,460,10) #点击第一张图片 270 | time.sleep(2) 271 | driver.swipe(350, 570, 350, 570, 10) # 点击一下图片 272 | time.sleep(1) 273 | 274 | comment_button = driver.page_source.find('评论吧') #判断页面是否有评论点赞等元素 275 | if comment_button != -1: 276 | driver.find_element_by_id('com.ugirls.app02:id/favorite').click() # 点赞 277 | time.sleep(0.3) 278 | driver.find_element_by_id('com.ugirls.app02:id/comment').click() #点击评论框 279 | time.sleep(1) 280 | driver.find_element_by_id('com.ugirls.app02:id/edit_chat').send_keys('可以可以可以') # 输入评论内容 281 | driver.find_element_by_id('com.ugirls.app02:id/btn_send').click() # 点击"发送评论按钮" 282 | time.sleep(1) 283 | else: 284 | driver.swipe(350, 570, 350, 570, 10) # 点击一下图片 285 | time.sleep(1) 286 | driver.find_element_by_id('com.ugirls.app02:id/favorite').click() # 点赞 287 | time.sleep(0.3) 288 | driver.find_element_by_id('com.ugirls.app02:id/comment').click() # 点击评论框 289 | time.sleep(1) 290 | driver.find_element_by_id('com.ugirls.app02:id/edit_chat').send_keys('可以可以可以') # 输入评论内容 291 | driver.find_element_by_id('com.ugirls.app02:id/btn_send').click() # 点击"发送评论按钮" 292 | time.sleep(1) 293 | 294 | driver.keyevent(4) 295 | 296 | #----------个人中心---------- 297 | driver.find_element_by_id('com.ugirls.app02:id/ibt_mine').click() # 点击个人中心 298 | time.sleep(1) 299 | driver.find_element_by_xpath('//android.widget.LinearLayout[contains(@index,5)]').click()#点击每日任务 300 | time.sleep(1) 301 | driver.get_screenshot_as_file(screenshot_path + '尤果圈.jpg') # 每日任务完成情况截图 302 | 303 | self.assertInNot('%',driver.page_source, msg='任务有失败,请到截图目录查看截图') 304 | 305 | 306 | 307 | # def test_007_taobao(self): 308 | # '''淘宝每日淘金币''' 309 | # # ----------启动应用---------- 310 | # AppTask.basic('com.taobao.taobao', 'com.taobao.tao.welcome.Welcome') 311 | # 312 | # # ----------领淘金币---------- 313 | # driver.find_element_by_name(u'领金币').click() # 点击主页中间的领金币 314 | # time.sleep(15) 315 | # 316 | # driver.swipe(354, 321, 354, 321, 10) # 点击中间的金币箱图片 317 | # time.sleep(3) 318 | # driver.get_screenshot_as_file(screenshot_path + '领取淘金币.jpg') # 签到完成后截图保存 319 | # print('签到完成') -------------------------------------------------------------------------------- /HTMLTestRunner.py: -------------------------------------------------------------------------------- 1 | """ 2 | A TestRunner for use with the Python unit testing framework. It 3 | generates a HTML report to show the result at a glance. 4 | 5 | The simplest way to use this is to invoke its main method. E.g. 6 | 7 | import unittest 8 | import HTMLTestRunner 9 | 10 | ... define your tests ... 11 | 12 | if __name__ == '__main__': 13 | HTMLTestRunner.main() 14 | 15 | 16 | For more customization options, instantiates a HTMLTestRunner object. 17 | HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. 18 | 19 | # output to a file 20 | fp = file('my_report.html', 'wb') 21 | runner = HTMLTestRunner.HTMLTestRunner( 22 | stream=fp, 23 | title='My unit test', 24 | description='This demonstrates the report output by HTMLTestRunner.' 25 | ) 26 | 27 | # Use an external stylesheet. 28 | # See the Template_mixin class for more customizable options 29 | runner.STYLESHEET_TMPL = '' 30 | 31 | # run the test 32 | runner.run(my_test_suite) 33 | 34 | 35 | ------------------------------------------------------------------------ 36 | Copyright (c) 2004-2007, Wai Yip Tung 37 | All rights reserved. 38 | 39 | Redistribution and use in source and binary forms, with or without 40 | modification, are permitted provided that the following conditions are 41 | met: 42 | 43 | * Redistributions of source code must retain the above copyright notice, 44 | this list of conditions and the following disclaimer. 45 | * Redistributions in binary form must reproduce the above copyright 46 | notice, this list of conditions and the following disclaimer in the 47 | documentation and/or other materials provided with the distribution. 48 | * Neither the name Wai Yip Tung nor the names of its contributors may be 49 | used to endorse or promote products derived from this software without 50 | specific prior written permission. 51 | 52 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 53 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 54 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 55 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 56 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 57 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 58 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 59 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 60 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 61 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 62 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 | """ 64 | 65 | # URL: http://tungwaiyip.info/software/HTMLTestRunner.html 66 | 67 | __author__ = "Wai Yip Tung" 68 | __version__ = "0.8.2" 69 | 70 | 71 | """ 72 | Change History 73 | 74 | Version 0.8.2 75 | * Show output inline instead of popup window (Viorel Lupu). 76 | 77 | Version in 0.8.1 78 | * Validated XHTML (Wolfgang Borgert). 79 | * Added description of test classes and test cases. 80 | 81 | Version in 0.8.0 82 | * Define Template_mixin class for customization. 83 | * Workaround a IE 6 bug that it does not treat 290 | 291 | %(heading)s 292 | %(report)s 293 | %(ending)s 294 | 295 | 296 | 297 | """ 298 | # variables: (title, generator, stylesheet, heading, report, ending) 299 | 300 | 301 | # ------------------------------------------------------------------------ 302 | # Stylesheet 303 | # 304 | # alternatively use a for external style sheet, e.g. 305 | # 306 | 307 | STYLESHEET_TMPL = """ 308 | 391 | """ 392 | 393 | 394 | 395 | # ------------------------------------------------------------------------ 396 | # Heading 397 | # 398 | 399 | HEADING_TMPL = """
400 |

%(title)s

401 | %(parameters)s 402 |

%(description)s

403 |
404 | 405 | """ # variables: (title, parameters, description) 406 | 407 | HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

408 | """ # variables: (name, value) 409 | 410 | 411 | 412 | # ------------------------------------------------------------------------ 413 | # Report 414 | # 415 | 416 | REPORT_TMPL = """ 417 |

Show 418 | Summary 419 | Failed 420 | All 421 |

422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | %(test_list)s 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 |
Test Group/Test caseCountPassFailErrorView
Total%(count)s%(Pass)s%(fail)s%(error)s 
449 | """ # variables: (test_list, count, Pass, fail, error) 450 | 451 | REPORT_CLASS_TMPL = r""" 452 | 453 | %(desc)s 454 | %(count)s 455 | %(Pass)s 456 | %(fail)s 457 | %(error)s 458 | Detail 459 | 460 | """ # variables: (style, desc, count, Pass, fail, error, cid) 461 | 462 | 463 | REPORT_TEST_WITH_OUTPUT_TMPL = r""" 464 | 465 |
%(desc)s
466 | 467 | 468 | 469 | 470 | %(status)s 471 | 472 | 481 | 482 | 483 | 484 | 485 | """ # variables: (tid, Class, style, desc, status) 486 | 487 | 488 | REPORT_TEST_NO_OUTPUT_TMPL = r""" 489 | 490 |
%(desc)s
491 | %(status)s 492 | 493 | """ # variables: (tid, Class, style, desc, status) 494 | 495 | 496 | REPORT_TEST_OUTPUT_TMPL = r""" 497 | %(id)s: %(output)s 498 | """ # variables: (id, output) 499 | 500 | 501 | 502 | # ------------------------------------------------------------------------ 503 | # ENDING 504 | # 505 | 506 | ENDING_TMPL = """
 
""" 507 | 508 | # -------------------- The end of the Template class ------------------- 509 | 510 | 511 | TestResult = unittest.TestResult 512 | 513 | class _TestResult(TestResult): 514 | # note: _TestResult is a pure representation of results. 515 | # It lacks the output and reporting ability compares to unittest._TextTestResult. 516 | 517 | def __init__(self, verbosity=1): 518 | TestResult.__init__(self) 519 | self.stdout0 = None 520 | self.stderr0 = None 521 | self.success_count = 0 522 | self.failure_count = 0 523 | self.error_count = 0 524 | self.verbosity = verbosity 525 | 526 | # result is a list of result in 4 tuple 527 | # ( 528 | # result code (0: success; 1: fail; 2: error), 529 | # TestCase object, 530 | # Test output (byte string), 531 | # stack trace, 532 | # ) 533 | self.result = [] 534 | 535 | 536 | def startTest(self, test): 537 | TestResult.startTest(self, test) 538 | # just one buffer for both stdout and stderr 539 | self.outputBuffer = io.StringIO() 540 | stdout_redirector.fp = self.outputBuffer 541 | stderr_redirector.fp = self.outputBuffer 542 | self.stdout0 = sys.stdout 543 | self.stderr0 = sys.stderr 544 | sys.stdout = stdout_redirector 545 | sys.stderr = stderr_redirector 546 | 547 | 548 | def complete_output(self): 549 | """ 550 | Disconnect output redirection and return buffer. 551 | Safe to call multiple times. 552 | """ 553 | if self.stdout0: 554 | sys.stdout = self.stdout0 555 | sys.stderr = self.stderr0 556 | self.stdout0 = None 557 | self.stderr0 = None 558 | return self.outputBuffer.getvalue() 559 | 560 | 561 | def stopTest(self, test): 562 | # Usually one of addSuccess, addError or addFailure would have been called. 563 | # But there are some path in unittest that would bypass this. 564 | # We must disconnect stdout in stopTest(), which is guaranteed to be called. 565 | self.complete_output() 566 | 567 | 568 | def addSuccess(self, test): 569 | self.success_count += 1 570 | TestResult.addSuccess(self, test) 571 | output = self.complete_output() 572 | self.result.append((0, test, output, '')) 573 | if self.verbosity > 1: 574 | sys.stderr.write('ok ') 575 | sys.stderr.write(str(test)) 576 | sys.stderr.write('\n') 577 | else: 578 | sys.stderr.write('.') 579 | 580 | def addError(self, test, err): 581 | self.error_count += 1 582 | TestResult.addError(self, test, err) 583 | _, _exc_str = self.errors[-1] 584 | output = self.complete_output() 585 | self.result.append((2, test, output, _exc_str)) 586 | if self.verbosity > 1: 587 | sys.stderr.write('E ') 588 | sys.stderr.write(str(test)) 589 | sys.stderr.write('\n') 590 | else: 591 | sys.stderr.write('E') 592 | 593 | def addFailure(self, test, err): 594 | self.failure_count += 1 595 | TestResult.addFailure(self, test, err) 596 | _, _exc_str = self.failures[-1] 597 | output = self.complete_output() 598 | self.result.append((1, test, output, _exc_str)) 599 | if self.verbosity > 1: 600 | sys.stderr.write('F ') 601 | sys.stderr.write(str(test)) 602 | sys.stderr.write('\n') 603 | else: 604 | sys.stderr.write('F') 605 | 606 | 607 | class HTMLTestRunner(Template_mixin): 608 | """ 609 | """ 610 | def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): 611 | self.stream = stream 612 | self.verbosity = verbosity 613 | if title is None: 614 | self.title = self.DEFAULT_TITLE 615 | else: 616 | self.title = title 617 | if description is None: 618 | self.description = self.DEFAULT_DESCRIPTION 619 | else: 620 | self.description = description 621 | 622 | self.startTime = datetime.datetime.now() 623 | 624 | 625 | def run(self, test): 626 | "Run the given test case or test suite." 627 | result = _TestResult(self.verbosity) 628 | test(result) 629 | self.stopTime = datetime.datetime.now() 630 | self.generateReport(test, result) 631 | print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)) 632 | return result 633 | 634 | 635 | def sortResult(self, result_list): 636 | # unittest does not seems to run in any particular order. 637 | # Here at least we want to group them together by class. 638 | rmap = {} 639 | classes = [] 640 | for n,t,o,e in result_list: 641 | cls = t.__class__ 642 | if not cls in rmap: 643 | rmap[cls] = [] 644 | classes.append(cls) 645 | rmap[cls].append((n,t,o,e)) 646 | r = [(cls, rmap[cls]) for cls in classes] 647 | return r 648 | 649 | 650 | def getReportAttributes(self, result): 651 | """ 652 | Return report attributes as a list of (name, value). 653 | Override this to add custom attributes. 654 | """ 655 | startTime = str(self.startTime)[:19] 656 | duration = str(self.stopTime - self.startTime) 657 | status = [] 658 | if result.success_count: status.append('Pass %s' % result.success_count) 659 | if result.failure_count: status.append('Failure %s' % result.failure_count) 660 | if result.error_count: status.append('Error %s' % result.error_count ) 661 | if status: 662 | status = ' '.join(status) 663 | else: 664 | status = 'none' 665 | return [ 666 | ('Start Time', startTime), 667 | ('Duration', duration), 668 | ('Status', status), 669 | ] 670 | 671 | 672 | def generateReport(self, test, result): 673 | report_attrs = self.getReportAttributes(result) 674 | generator = 'HTMLTestRunner %s' % __version__ 675 | stylesheet = self._generate_stylesheet() 676 | heading = self._generate_heading(report_attrs) 677 | report = self._generate_report(result) 678 | ending = self._generate_ending() 679 | output = self.HTML_TMPL % dict( 680 | title = saxutils.escape(self.title), 681 | generator = generator, 682 | stylesheet = stylesheet, 683 | heading = heading, 684 | report = report, 685 | ending = ending, 686 | ) 687 | self.stream.write(output.encode('utf8')) 688 | 689 | 690 | def _generate_stylesheet(self): 691 | return self.STYLESHEET_TMPL 692 | 693 | 694 | def _generate_heading(self, report_attrs): 695 | a_lines = [] 696 | for name, value in report_attrs: 697 | line = self.HEADING_ATTRIBUTE_TMPL % dict( 698 | name = saxutils.escape(name), 699 | value = saxutils.escape(value), 700 | ) 701 | a_lines.append(line) 702 | heading = self.HEADING_TMPL % dict( 703 | title = saxutils.escape(self.title), 704 | parameters = ''.join(a_lines), 705 | description = saxutils.escape(self.description), 706 | ) 707 | return heading 708 | 709 | 710 | def _generate_report(self, result): 711 | rows = [] 712 | sortedResult = self.sortResult(result.result) 713 | for cid, (cls, cls_results) in enumerate(sortedResult): 714 | # subtotal for a class 715 | np = nf = ne = 0 716 | for n,t,o,e in cls_results: 717 | if n == 0: np += 1 718 | elif n == 1: nf += 1 719 | else: ne += 1 720 | 721 | # format class description 722 | if cls.__module__ == "__main__": 723 | name = cls.__name__ 724 | else: 725 | name = "%s.%s" % (cls.__module__, cls.__name__) 726 | doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" 727 | desc = doc and '%s: %s' % (name, doc) or name 728 | 729 | row = self.REPORT_CLASS_TMPL % dict( 730 | style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', 731 | desc = desc, 732 | count = np+nf+ne, 733 | Pass = np, 734 | fail = nf, 735 | error = ne, 736 | cid = 'c%s' % (cid+1), 737 | ) 738 | rows.append(row) 739 | 740 | for tid, (n,t,o,e) in enumerate(cls_results): 741 | self._generate_report_test(rows, cid, tid, n, t, o, e) 742 | 743 | report = self.REPORT_TMPL % dict( 744 | test_list = ''.join(rows), 745 | count = str(result.success_count+result.failure_count+result.error_count), 746 | Pass = str(result.success_count), 747 | fail = str(result.failure_count), 748 | error = str(result.error_count), 749 | ) 750 | return report 751 | 752 | 753 | def _generate_report_test(self, rows, cid, tid, n, t, o, e): 754 | # e.g. 'pt1.1', 'ft1.1', etc 755 | has_output = bool(o or e) 756 | tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) 757 | name = t.id().split('.')[-1] 758 | doc = t.shortDescription() or "" 759 | desc = doc and ('%s: %s' % (name, doc)) or name 760 | tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL 761 | 762 | # o and e should be byte string because they are collected from stdout and stderr? 763 | if isinstance(o,str): 764 | # TODO: some problem with 'string_escape': it escape \n and mess up formating 765 | # uo = unicode(o.encode('string_escape')) 766 | uo = o 767 | else: 768 | uo = o 769 | if isinstance(e,str): 770 | # TODO: some problem with 'string_escape': it escape \n and mess up formating 771 | # ue = unicode(e.encode('string_escape')) 772 | ue = e 773 | else: 774 | ue = e 775 | 776 | script = self.REPORT_TEST_OUTPUT_TMPL % dict( 777 | id = tid, 778 | output = saxutils.escape(uo+ue), 779 | ) 780 | 781 | row = tmpl % dict( 782 | tid = tid, 783 | Class = (n == 0 and 'hiddenRow' or 'none'), 784 | style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), 785 | desc = desc, 786 | script = script, 787 | status = self.STATUS[n], 788 | ) 789 | rows.append(row) 790 | if not has_output: 791 | return 792 | 793 | def _generate_ending(self): 794 | return self.ENDING_TMPL 795 | 796 | 797 | ############################################################################## 798 | # Facilities for running tests from the command line 799 | ############################################################################## 800 | 801 | # Note: Reuse unittest.TestProgram to launch test. In the future we may 802 | # build our own launcher to support more specific command line 803 | # parameters like test title, CSS, etc. 804 | class TestProgram(unittest.TestProgram): 805 | """ 806 | A variation of the unittest.TestProgram. Please refer to the base 807 | class for command line parameters. 808 | """ 809 | def runTests(self): 810 | # Pick HTMLTestRunner as the default test runner. 811 | # base class's testRunner parameter is not useful because it means 812 | # we have to instantiate HTMLTestRunner before we know self.verbosity. 813 | if self.testRunner is None: 814 | self.testRunner = HTMLTestRunner(verbosity=self.verbosity) 815 | unittest.TestProgram.runTests(self) 816 | 817 | main = TestProgram 818 | 819 | ############################################################################## 820 | # Executing this module from the command line 821 | ############################################################################## 822 | 823 | if __name__ == "__main__": 824 | main(module=None) 825 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | find_element_by_xpath 123 | TouchAction 124 | driver 125 | self.driver 126 | 4 127 | 请到截图目录查看截图' + str(screenshot_path) 128 | key 129 | .png 130 | ,jpg 131 | div> 132 | 133 | 134 | self.driver 135 | driver 136 | 请到截图目录查看截图' 137 | ,jpg 138 | .jpg 139 | p> 140 | 141 | 142 | 143 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 169 | 170 | 171 | 172 | 173 | true 174 | DEFINITION_ORDER 175 | 176 | 177 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 |