完整个人网站开发案例,做的网站怎么让百度收录,闪图在线制作网站,好看的单页面网站一#xff1a;主要内容
框架功能、框架架构及测试报告效果
airtest安装、环境搭建
框架搭建、框架运行说明
框架源码
二#xff1a;框架功能及测试报告效果
1. 框架功能#xff1a;
该框架笔者用来作为公司的项目的前端自动化#xff0c;支持pc和app#xff0c;本文…一主要内容
框架功能、框架架构及测试报告效果
airtest安装、环境搭建
框架搭建、框架运行说明
框架源码
二框架功能及测试报告效果
1. 框架功能
该框架笔者用来作为公司的项目的前端自动化支持pc和app本文的air脚本是针对app的关于pc的脚本会专门在写一篇文章说明该框架功能如下
支持在安卓多台设备中批量运行所有后缀为air的测试脚本因为ios的连接需要macOS我是windows机所以暂时只连了安卓端的ios未做测试
支持指定某个用例或某几个用例在某台设备或某几台设备中进行运行
支持控制测试用例执行顺序默认会将登录用例排在第一退出用例排在最后执行如果想要自定义其他顺序可以在run.py文件中修改sort_cases函数方法即可
支持多脚本多设备运行完成后生成一份汇总的测试报告且点击汇总测试报告中具体的某一个用例还能查看该用例详细的airtest报告
2. 框架架构说明 3. 测试报告效果
给大家看一下多设备、多脚本的测试报告效果 点击详情效果 三airtest安装、环境搭建
1.python环境安装
这里不再赘述安装并配置好环境变量后执行python -V查看是否安装成功 2.airtestIDE安装
airtest安装很简单安装airtestIDE从官网下载http://airtest.netease.com/
下载后解压缩到本地我的本地位置为G:\AirtestIDE_2020-01-21_py3_win64\AirtestIDE_2020-01-21_py3_win64\AirtestIDE.exe双击exe文件即为启动airtestIDE工具即可
3.包安装
需要安装如下包
pip install airtest
pip install pocoui如果执行不能安装成功则可以使用如下命令
pip install -i http://pypi.douban.com/simple --trusted-host pypi.douban.com airtest
pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com pocoui如果想用airtest编写selenium即pc自动化脚本则还需要安装如下包
pip install seleniumpip install pynputpip install airtest_selenium关于这一步的安装也就是 pip install airtest_selenium也可以从airtest安装目录下拷贝该文件夹到python目录下
我的python目录为G:\python3.6.5
我的airtest安装目录为G:\AirtestIDE_2020-01-21_py3_win64\AirtestIDE_2020-01-21_py3_win64该路径下有个airtest_selenium文件夹
可以拷贝airtest目录下的airtest_selenium文件夹到python目录下。
如果想用airtest编写selenium即pc自动化脚本除了安装上面的包因为airtest-selenium自动化因为需要打开浏览器所以我们还需要配置谷歌浏览器路径和下载匹配的谷歌驱动文件
airtest设置谷歌启动路径airtestIDE界面-点击选项-点击设置-点击chrome path-选择谷歌安装路径一直到chrome.exe文件 下载匹配的谷歌驱动文件
可以使用该网站下载https://npm.taobao.org/mirrors/chromedriver
下载后替换掉airtest根目录我的路径是G:\AirtestIDE_2020-01-21_py3_win64\AirtestIDE_2020-01-21_py3_win64下的chromedriver.exe文件即可
4.框架版本说明
该框架使用版本如下
python 3.6.5airtest 1.1.3pocoui 1.0.79pynput 1.6.8airtestIDE 1.2.3四框架搭建、框架运行说明
1.框架搭建
该框架搭建很简单就是一个python工程
该工程根目录下开始时有一个result空文件夹、一个report_tpl.html模板文件、run.py启动脚本、docs文件夹是我自己放的一些项目描述文档可有可无.air文件是自己通过airtestIDE编写的项目的自动化脚本 2.框架脚本文件说明
run.py #启动文件python run.py即可
report_tpl.html #测试报告模板文件
report.html #自动生成的测试报告文件会将汇总的执行结果的json数据即下面的summary数据格式与report_tpl.html结合生成测试报告
result #文件夹用于存放每个测试用例的执行json结果数据格式为下面的results数据格式
xxx.air #测试用例所有以.air文件名称结尾的文件夹都是测试用例
xxx.air/log #每个测试用例的日志文件以设备号区分每个设备号下存放一份测试结果日志文件log.html #每个测试用例在每个设备中运行的具体效果即测试报告中点击具体测试用例右侧弹出的页面详情效果log.txt #每个测试用例在每个设备中运行的json结果数据3.框架运行编写建议
执行命令时可以用python run.py运行整个框架
但是写脚本或者调试脚本时用airtestIDE来操作即从airtestIDE中新建编辑.air脚本保存到该框架的根目录下调试通过后再用run.py进行批量脚本、批量设备去执行。
这样就比较清晰
五框架源码
1.run.py 1 # -*- encodingutf-8 -*-2 # Run Airtest in parallel on multi-device3 import os4 import traceback5 import subprocess6 import webbrowser7 import time8 import json9 import shutil10 from airtest.core.android.adb import ADB11 from jinja2 import Environment, FileSystemLoader12 13 14 def run(devices, airs):15 16 run_all17 18 19 20 try:21 data_r[]22 global time_s23 time_s time.time()24 for air in airs:25 results load_jdon_data(air)26 tasks run_on_multi_device(devices, air, results)27 for task in tasks:28 status task[process].wait()29 results[tests][task[dev]] run_one_report(task[air], task[dev])30 results[tests][task[dev]][status] status31 name air.split(.)[0]32 json.dump(results, open(get_path(result)os.sepname_data.json, w), indent4)33 data_r.append(results)34 run_summary(data_r)35 except Exception as e:36 traceback.print_exc()37 38 39 def run_on_multi_device(devices, air, results):40 41 在多台设备上运行airtest脚本42 Run airtest on multi-device43 44 tasks []45 for dev in devices:46 log_dir get_path(log,dev,air)47 #命令行执行airtest run openOrder.air --device Android://127.0.0.1:5037/b7f0c036 --log F:\airtest_code\good_store_project\log\openOrder48 cmd [49 airtest,50 run,51 air,52 --device,53 Android:/// dev,54 --log,55 log_dir56 ]57 try:58 tasks.append({59 process: subprocess.Popen(cmd, cwdos.getcwd()),60 dev: dev,61 air: air62 })63 except Exception as e:64 traceback.print_exc()65 return tasks66 67 #点击每个用例的详情页面68 def run_one_report(air, dev):69 70 生成一个脚本的测试报告71 Build one test report for one air script72 73 try:74 log_dir get_path(log,dev, air)75 log os.path.join(log_dir, log.txt)76 if os.path.isfile(log):77 #命令行执行airtest report F:\airtest_code\good_store_project\openOrder.air --log_root F:\airtest_code\good_store_project\log\openOrder --outfile F:\airtest_code\good_store_project\log\openOrder\openOrder.html --lang zh78 #如果是selenium则最后要加上selenium插件79 #airtest report F:\airtest_code\good_store_project\openOrder.air --log_root F:\airtest_code\good_store_project\log\openOrder --outfile F:\airtest_code\good_store_project\log\openOrder\openOrder.html --lang zh --plugins airtest_selenium.report80 cmd [81 airtest,82 report,83 air,84 --log_root,85 log_dir,86 --outfile,87 os.path.join(log_dir, log.html),88 --lang,89 zh90 ]91 ret subprocess.call(cmd, shellTrue, cwdos.getcwd())92 return {93 status: ret,94 path: os.path.join(log_dir, log.html)95 }96 else:97 print(Report build Failed. File not found in dir %s % log)98 except Exception as e:99 traceback.print_exc()
100 return {status: -1, device: dev, path: }
101
102
103 def run_summary(data):
104
105 生成汇总的测试报告
106 Build sumary test report
107
108 try:
109 for i in data:
110 c get_json_value_by_key(i,status)
111
112 summary {
113 time: %.3f % (time.time() - time_s),
114 success: c.count(0),
115 count: len(c)
116 }
117 summary[start_all] time.strftime(%Y-%m-%d %H:%M:%S,
118 time.localtime(time_s))
119 summary[result] data
120 print(summary,summary)
121
122 env Environment(loaderFileSystemLoader(os.getcwd()),
123 trim_blocksTrue)
124 html env.get_template(report_tpl.html).render(datasummary)
125 with open(report.html, w, encodingutf-8) as f:
126 f.write(html)
127 webbrowser.open(report.html)
128 except Exception as e:
129 traceback.print_exc()
130
131
132 def load_jdon_data(air):
133
134 加载进度
135 返回一个空的进度数据
136
137 clear_log_dir(air)
138 return {
139 start: time.time(),
140 script: air,
141 tests: {}
142
143 }
144
145 def clear_log_dir(air):
146
147 清理log文件夹 openCard.air/log
148 Remove folder openCard.air/log
149
150 log os.path.join(os.getcwd(), air, log)
151 if os.path.exists(log):
152 shutil.rmtree(log)
153
154 #获取key为status的值
155 def get_json_value_by_key(in_json, target_key, results[]):
156 for key,value in in_json.items(): # 循环获取key,value
157 if key target_key:
158 results.append(value)
159 if isinstance(value, dict):
160 get_json_value_by_key(value,target_key)
161 return results
162
163 #获取路径
164 def get_path(content,deviceNone,airopenCard.air):
165 root_path os.getcwd()
166 path os.getcwd()
167 if contentresult:
168 #返回测试报告路径
169 path os.path.join(root_path,result)
170 elif content log:
171 log_dir os.path.join(root_path,air, log, device.replace(., _).replace(:, _))
172 #如果没有日志路径则创建一个
173 if not os.path.exists(log_dir):
174 os.makedirs(log_dir)
175 #返回日志路径
176 path log_dir
177 elif content cases:
178 #返回测试用例路径
179 path os.path.join(root_path,air)
180 else:
181 #返回根目录
182 path root_path
183 return path
184
185 #获取路径下所有air的测试用例文件
186 def get_cases(path):
187 cases[]
188 for name in os.listdir(get_path(path)): # 遍历当前路径下的文件夹和文件名称
189 if name.endswith(.air):
190 cases.append(name)
191 return cases
192
193 def sort_cases(cases,loginAir,outAir):
194 #清除列表中的登录、退出登录然后将其分别添加到列表的第一位和最后一位
195 cases.remove(loginAir)
196 cases.remove(outAir)
197 cases.insert(0, loginAir)
198 cases.insert(len(airs), outAir)
199 return cases
200
201
202 if __name__ __main__:
203
204
205 初始化数据
206 Init variables here
207
208 #获取所有已连接的设备列表
209 devices [tmp[0] for tmp in ADB().devices()]
210 #设置指定设备执行测试用例
211 # devices [BTY4C16705003852,b7f0c036]
212 #获取所有测试用例
213 airs get_cases(root)
214 #将登录用例排在最前面执行退出用例排在最后面执行
215 sort_airs sort_cases(airs,loginPro.air,loginOutPro.air)
216 #获取指定用例,按顺序执行
217 # sort_airs [openCardPro.air,openOrderPro.air,quickMoneyPro.air]
218
219 执行脚本
220 excute scripts
221
222 # 运行所有脚本
223 run(devices, sort_airs)2.report_tpl.html 1 !DOCTYPE html2 html3 head4 meta http-equivX-UA-Compatible contentIEedge5 link relshortcut icon typeimage/png hrefhttp://airtest.netease.com/static/img/icon/favicon.ico6 script srchttps://code.jquery.com/jquery-3.1.1.min.js/script7 meta nameviewport contentwidthdevice-width, initial-scale18 meta http-equivContent-Type contenttext/html; charsetutf-8 / 9 titleAirtest 多设备并行测试结果汇总/title10 /head11 style typetext/css12 *{13 margin: 0;14 padding: 0;15 }16 body{17 background: #eeeeee18 }19 .container {20 width: 75%;21 min-width: 800px;22 margin: auto23 }24 body.zh .en{25 display: none;26 }27 body.en .zh{28 display: none;29 }30 h1{31 margin-top: 50px;32 text-align: center;33 }34 .center{35 text-align: center;36 margin-top: 15px;37 margin-bottom: 30px;38 font-size: 14px;39 position: relative;40 }41 .btn{42 border: solid 1px #c0c0c0;43 padding: 5px 20px;44 border-radius: 3px;45 background: white;46 cursor: context-menu;47 }48 .btn.lang:hover {49 background: #5cb85c26;50 border-color: #0a790a;51 }52 .btn.lang {53 position: absolute;54 top: 0;55 }56 .head {57 margin: 20px 0 30px 0;58 }59 .head, .table{60 background: white;61 border-radius: 5px;62 box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);63 padding: 30px 20px;64 65 }66 .head .progress{67 background: #dddddd;68 color: white;69 border-radius: 5px;70 text-align: center;71 margin-top: 12px;72 }73 .head .progress-bar-success{74 width: 0;75 transition: all 0.5s ease;76 background: #5cb85c;77 border-radius: 5px;78 }79 .table-title {80 text-align: center;81 margin-bottom: 20px;82 font-size: 18px;83 font-weight: bold;84 position: relative;85 }86 .table-row{87 border: solid 1px #e5e5e5;88 margin-top: -1px;89 cursor: context-menu;90 }91 .table-row:hover, .table-row.active{92 background: beige;93 }94 .table-head{95 background: aliceblue;96 }97 .table-head:hover{98 background: aliceblue;99 }
100 .table-head .table-col{
101 padding-top: 10px;
102 padding-bottom: 10px;
103 font-weight: bold;
104 text-align: center;
105 }
106 .table-col{
107 display: inline-block;
108 width: 200px;
109 line-height: 30px;
110 padding: 5px 10px;
111 border-left: solid 1px #e5e5e5;
112 margin-top: -1px;
113 margin-right: -5px;
114 }
115 .table-col.short{
116 width: 100px;
117 text-align: center;
118 }
119 .table-col.mid{
120 width: 200px;
121 text-align: center;
122 }
123 .table-col:first-child{
124 border: none;
125 }
126 .table-col.long{
127 width: calc(100% - 700px);
128 }
129 .table-col.success{
130 color: green;
131 }
132 .table-col.failed{
133 color: red;
134 }
135 .detail{
136 text-align: center;
137 font-size: 14px;
138 color: gray;
139 }
140 .iframe{
141 position: fixed;
142 top: 0;
143 right: -100%;
144 width: 70%;
145 min-width: 800px;
146 height: 100%;
147 box-shadow: 0 5px 10px grey;
148 transition: right 0.5s ease;
149 background: white;
150 max-width: 1100px;
151 }
152 .iframe-tools{
153 position: absolute;
154 top: 23px;
155 left: -34px;
156 background: white;
157 box-shadow: -2px 2px 5px grey;
158 border-radius: 7px;
159 }
160 .iframe-tools .close, .iframe-tools .open{
161 width: 32px;
162 height: 50px;
163 color: gray;
164 cursor: context-menu;
165 display: block;
166 }
167 .iframe.show{
168 right: 0;
169 }
170 iframe{
171 width: 100%;
172 height: calc(100% - 70px);
173 border: none;
174 }
175 .iframe-head {
176 height: 60px;
177 line-height: 70px;
178 text-align: center;
179 border-bottom: solid 1px #ddd;
180 box-shadow: 2px 0 6px #999;
181 margin-bottom: 10px;
182 }
183 ::-webkit-scrollbar {
184 width: 10px;
185 height: 10px;
186 background-color: rgba(0,0,0,.34);
187 }
188 ::-webkit-scrollbar-thumb {
189 background-color: #8b8b8b;
190 border-radius: 10px;
191 }
192 ::-webkit-scrollbar-track {
193 background-color: #f5f5f5;
194 -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.22);
195 }
196 .iframe .close {
197 background: url(data:img/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAYAAAAj6qa3AAAABGdBTUEAALGPC/xhBQAAAAFzUkdC AK9OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dE AAAAAAAAUO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAA6JJREFUaN7tmc9LG0EUx99sExB/wBJ vQhCPLTojArAf6iKJSSVhD0EhNC1HgWxIt/gBA0MaQXqQoV2gb/AgKrs4KHlSMN38EFRGkIupm Xw/reEgIu9lsNi34vQTcie99P/NmZt8E4FWvKkpt6bZ0W/rdu0rnYVdektmBNEETNPHlS3Ytu5Zd Oz6mC3SBLgwMVNo4o4wy2tmp1qv1av3REVthK2xldNS2ANRN3dT99SvVqEa1pyfGGGMMkcpUprKq VgqEME436SbdvL8XebEAC7CAppkFQYyMwwM8wMP376SVtJJWlyt3HMYwhrFsFg7hEA6HhpRBZVAZ XFwst3GcwzmcS6VImIRJO3b/IHAgCFCB3RARyTCfdzHfdGoIQCxlkSpQwxiEKuqMkoMt3Ebt1UV kpCE5MCAElACSuDHD7uMN280bzRvdHcTiUhEvWroPHcvJ4nCBuxERvfv9t2a3ZrTk4EM/z9gC5 QW6QG05PyR7ZI3uitJejAK9VEgTNEHT0pJdS0PMeLHGRQWQEAmR0OhorvGXvI3T8tYy1jL2OfP uI7ruL68rP/V7TZL3urSMF3qBYwDBw48HOacc85jsULDDQE4DcIp40UDKDcIp41bBmA3CDJDZsjM 1ZXTxksG8JIHZZRRnw93cAd3lpYKHZd5IMSpIYMMsqqaNh6EIAQ1DeqgDuoCAd7De3jPt29W8y8Z gJDVijAtm2Y8V2/syu9863zrfOvgwOPzDy/X04gRM4fTpOYz1OGUybjsA20GU2biQ6WaoWGnV WrVW/ecPzuIszmazxX4f/ehHv6ZhBCMYub0tV5627QFCdIJO0ImuLuiDPuj7/dv05lYIRJl7DdsA 2G3cKRAlA7DapIjjTJR60cenTU2X5T1AzLjVJgXmYR7mR0akcWlcGu/v1x8633QVXQGWS91gV69U 02UaQLmM58ppEIYAnDJeKRCFr8QqZNxpEAWvxNRr9Vq9TqeLblLiEId4MMgVrnAlmbRqPI9riU0X 3uEd3n34kHszlPdqehY9i55Fb29re2t7a3svL4mXeIn340d9RklxYgZX4VVWB0Z0Y0nEnYZF8pk MplMZn/fcO58dyYf8UmLuIirqkppV1pV9p//sx7bhSYpmiKpoaHyQW5IBfxuH58SZJT7qFZLg0 QhCC0OQk93M/909PlxxQgBC/Bj38KGQU4aNQOj5PD7qn5OTZQuoB/B6K238f8nrVf6/gLOvYPg ZwC/JwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wMy0wNlQyMDozMTo1NCswODowMMqAOUgAAAAl dEVYdGRhdGU6bW9kaWZ5ADIwMTktMDMtMDZUMjA6MzE6NTQrMDg6MDC73YH0AAAASHRFWHRzdmc6 YmFzZS11cmkAZmlsZTovLy9ob21lL2FkbWluL2ljb24tZm9udC90bXAvaWNvbl9jOHk0dXZzNXd0 Zy9DbG9zZS5zdmfc199nAAAAAElFTkSuQmCC) no-repeat;
198 background-size: 20px;
199 background-position: center;
200 border-bottom: solid 2px #e5e5e5;
201 }
202 .iframe .open {
203 background: url(data:img/jpg;base64,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 aGFyZS5zdmftz7m3AAAAAElFTkSuQmCC) no-repeat;
204 background-size: 20px;
205 background-position: center;
206 }
207 select{height: 28px; line-height: auto; vertical-align: middle; height: 22px\9; padding: 3px 0\9; box-sizing:content-box; font-size: 13px;}
208 :root select{padding: 0; height: 28px;}
209 /style
210 body classzh
211 div classcontainer-fluid
212 div classcontainer
213 div classmain
214 div classmaterial
215 h1汇总报告/h1
216 div classcenter
217 div classbtn langSwitch to English version/div
218 div classtime zh开始时间{{data[start_all]}}耗时 b{{data[time]}}/b 秒/div
219 div classtime enStarted at{{data[start_all]}}cost b{{data[time]}}/b s/div
220 /div
221 div classhead
222 header classzhspan classrate/span成功率/span {{data[success]}}/{{data[count]}}/header
223 header classenspan classrateSuccess rate/span {{data[success]}}/{{data[count]}}/header
224 div
225 div classprogress
226 div classprogress-bar progress-bar-success roleprogressbar aria-valuemin0 aria-valuemax100 stylewidth: {{data[success] *100 / data[count]}}%
227 span class{{%0.2f % (data[success] *100 / data[count])}}%/span
228 /div
229 /div
230 /div
231 /div
232 select name idexit stylewidth: 100px;
233 option classzh valueall全部/option
234 option classen valueallall/option
235 option classen value成功success/option
236 option classzh value成功成功/option
237 option classen value失败failed/option
238 option classzh value失败失败/option
239 /select
240 div classtable
241 div classtable-title
242 span classrunning_detail zh用例列表/span
243 span classrunning_detail enDetail/span
244 /div
245 div classtable-content idtab
246 div classtable-row table-head
247 div classtable-col short zh序号/div
248 div classtable-col short zh状态/div
249 div classtable-col mid zh用例/div
250 div classtable-col long zh设备/div
251 div classtable-col short enid/div
252 div classtable-col short enresult/div
253 div classtable-col mid encase/div
254 div classtable-col long endevice/div
255 div classtable-col --/div
256 /div
257 {% set ns namespace(found0) %}
258 {% for dat in data[result] %}
259 {% for dev, item in dat[tests].items() %}
260 div classtable-row path{{item[path]}}
261 {% set ns.found ns.found 1 %}
262 div classtable-col short{{ns.found}}/div
263 div classtable-col short zh {{success if item[status]0 else failed}}{{成功 if item[status]0 else 失败}}/div
264 div classtable-col short en {{success if item[status]0 else failed}}{{success if item[status]0 else failed}}/div
265 div classtable-col mid{{dat[script]}}/div
266 div classtable-col long{{dev}}/div
267
268 div classtable-col detail zh点击可查看详情/div
269 div classtable-col detail enclick to see detail/div
270 /div
271 {% endfor %}
272 {% endfor %}
273 /div
274 /div
275 /div
276 /div
277
278 div classiframe
279 div classiframe-head/div
280 iframe src./iframe
281 div classiframe-tools
282 div classclose/div
283 a classopen href. target_blank/a
284 /div
285 /div
286 /div
287 /body
288 script typetext/javascript
289 var Lang zh // or en
290 var rows document.querySelectorAll(.table-row)
291 var iframe document.querySelector(.iframe)
292 var iframeHead document.querySelector(.iframe-head)
293 var open document.querySelector(.open)
294 var close document.querySelector(.iframe .close)
295 var langBtn document.querySelector(.lang)
296 var body document.body
297 var prevActiveRow null
298 function init() {
299 for(i0; irows.length; i){
300 addEvent(rows[i], click, function(e){
301 path this.getAttribute(path)
302 console.log(this)
303 if(path) {
304 showIframe(this)
305 }
306 })
307 }
308 addEvent(close, click, function(e){
309 iframe.classNameiframe
310 })
311 addEvent(langBtn, click, function(e){
312 if(Lang zh){
313 Lang en;
314 this.innerText 切换到中文版
315 } else {
316 Lang zh
317 this.innerText Switch to English version
318 }
319 document.body.className Lang
320 if (iframe.className.indexOf(show)0) {
321 showIframe(prevActiveRow)
322 }
323 })
324 document.body.className Lang
325 }
326 function showIframe(obj){
327 var num obj.querySelector(.table-col.short).innerText
328 var device obj.querySelector(.table-col.long).innerText
329 if(Lang en) {
330 num ordinal_suffix_of(num)
331 iframeHead.innerHTML Test report running in the num device device
332 open.setAttribute(title, open in a new tab)
333 close.setAttribute(title, close)
334 }
335 else {
336 iframeHead.innerHTML 第 num 台设备 【 device 】 的测试报告
337 open.setAttribute(title, 在新标签页打开)
338 close.setAttribute(title, 关闭)
339 }
340 iframe.querySelector(iframe).setAttribute(src, path)
341 open.setAttribute(href, path)
342 iframe.classNameiframe show
343 if(prevActiveRow){
344 prevActiveRow.className table-row
345 }
346 obj.className table-row active
347 prevActiveRow obj
348 }
349 function ordinal_suffix_of(i) {
350 i Number(i)
351 var j i % 10,
352 k i % 100;
353 if (j 1 k ! 11) {
354 return i st;
355 }
356 if (j 2 k ! 12) {
357 return i nd;
358 }
359 if (j 3 k ! 13) {
360 return i rd;
361 }
362 return i th;
363 }
364 function addEvent(obj,type,handle) {
365 try{// Chrome、FireFox、Opera、Safari、IE9.0 and above
366 obj.addEventListener(type,handle);
367 }catch(e){
368 try{// IE8.0 and below
369 obj.attachEvent(on type,handle);
370 }catch(e){// Browser in earlier vesion
371 obj[on type] handle;
372 }
373 }
374 }
375 init()
376 $(document).ready(function(){
377 $(#exit).change(function(){ // 下拉框绑定change事件
378 var exit_code $(this).children(option:selected).val(); // 获取下拉框选中值
379 $(#tab .table-row).each(function() {
380 var self $(this).children().eq(1).text(); // 获取每行第二列的值
381 if(exit_codeall){ // 选中all时数据全部显示
382 $(this).show();
383 }else{ // 选中其他的值时进一步判断
384 if(self!exit_code){ // 列中的值和选中值不一致
385 $(this).hide(); // 该行不显示
386 $(#tab .table-head).show()
387 }else{
388 $(this).show();
389 }
390 }
391 });
392 })
393 })
394 /script
395 /html最后 下方这份完整的软件测试视频教程已经整理上传完成需要的朋友们可以自行领取【保证100%免费】