Commit 530c0f65 authored by 李光宇's avatar 李光宇
Browse files

Merge branch 'master' into 'main'

代码归档只main分支

See merge request !1
parents 13b3da0e 2b18a6d3
# 登录内网测试环境地址
"spd3_login_url": "/hospitaltest2/spd/#/login?redirect=/paltform/info0/paltform/product/calatog/paltform/product/calatog"
"username": "//input[@placeholder='请输入用户名']"
"password": "//input[@placeholder='请输入密码']"
"login_button": "//span[text()='登 录']"
"name1": "xhs"
\ No newline at end of file
172.16.10.203 api.babytree-dev.com
172.16.10.203 api-test11.babytree-test.com
172.16.10.203 api.test11.babytree-fpm.com
172.16.10.203 api.babytree.com
# online
10.54.11.80 idb.baobaoshu.com
10.54.11.80 grafana.baobaoshu.com
10.54.11.80 zabbix.baobaoshu.com
10.54.11.80 cacti.baobaoshu.com
10.54.11.80 ucm.baobaoshu.com
10.54.11.80 rundeck.baobaoshu.com dubbo.baobaoshu.com
10.54.11.80 cmdb.baobaoshu.com
10.54.11.80 apollo.baobaoshu.com muse.baobaoshu.com
10.54.11.80 splunk.baobaoshu.com
172.16.10.203 test100.babytree-dev.com g.babytree-dev.com
122.9.41.244 gerrit.babytree-inc.com
10.54.17.153 localproxy.baobaoshu.com
192.168.24.43 gerrit.mtmm.com
10.17.65.22 case.cmic.com.cn
10.54.50.90 sso.baobaoshu.com
8.131.247.52 docp.plt.babytree-inc.com
user:
live: 6ea032c10ef24b10aa7fcfe5424446afin01
pre: 6ea032c10ef24b10aa7fcfe5424446afin01
health:
live: 6013c13418a245b9866e9cdec92ff9b9in01
pre : 6013c13418a245b9866e9cdec92ff9b9in01
sit: health
alimall:
live: f4d539e7b8204a8890929320f2052d68in01
pre : f4d539e7b8204a8890929320f2052d68in01
sit: alimall
promotion:
live: 9e33dd3e26a54c8caeaf871e873edb38in01
pre: 9e33dd3e26a54c8caeaf871e873edb38in01
sit: promotion
salesorder:
live: 7f9c968b57084c0782aeb620e2a064b1in01
pre : 7f9c968b57084c0782aeb620e2a064b1in01
sit: salesorder
seller:
live: 2888c1775daf4c60897073068b6c219ain01
pre: 2888c1775daf4c60897073068b6c219ain01
sit: seller
cms:
live: 790afd094355416895ca39891c09c0d3in01
pre: 790afd094355416895ca39891c09c0d3in01
sit: cms
community:
live: 790afd094355416895ca39891c09c0d3in01
pre: 790afd094355416895ca39891c09c0d3in01
sit: community
price:
live: 6ea032c10ef24b10aa7fcfe5424446afin01
pre: 6ea032c10ef24b10aa7fcfe5424446afin01
sit: price
medical:
live: 89a813f93c454db0a23c25f95c0d2e2cin01
pre: 89a813f93c454db0a23c25f95c0d2e2cin01
sit: medical
bcoin:
live: f4d539e7b8204a8890929320f2052d68in01
pre: f4d539e7b8204a8890929320f2052d68in01
sit: bcoin
edu:
live: 8e7f2572f92e49f4b0061f75682470eain01
pre: 8e7f2572f92e49f4b0061f75682470eain01
sit: edu
account:
live: 2888c1775daf4c60897073068b6c219ain01
pre: 2888c1775daf4c60897073068b6c219ain01
sit: account
finance:
live: 6013c13418a245b9866e9cdec92ff9b9in01
pre: 6013c13418a245b9866e9cdec92ff9b9in01
sit: finance
socialec:
live: 89a813f93c454db0a23c25f95c0d2e2cin01
pre: 89a813f93c454db0a23c25f95c0d2e2cin01
sit: socialec
item:
live: 2888c1775daf4c60897073068b6c219ain01
pre: 2888c1775daf4c60897073068b6c219ain01
sit: item
new_wms:
live: 89a813f93c454db0a23c25f95c0d2e2cin01
pre: 89a813f93c454db0a23c25f95c0d2e2cin01
sit: new_wms
distribution:
live: 8e7f2572f92e49f4b0061f75682470eain01
pre: 8e7f2572f92e49f4b0061f75682470eain01
sit: distribution
supplier:
live: 790afd094355416895ca39891c09c0d3in01
sit: supplier
local:
live: pc-2zerh8cqy748e33i8
sit : b9e5fbff-08e9-11ed-866f-00163e324a72
"dingding_msg":
- 59d455293168352cecfaa4fb67f2568f3fa54453a2fdb87ce9817c316dc41b57
"select_user_token":
- user
- select token from user.uc_user_token where enc_user_id=%s;
"exp_expert":
- medical
- SELECT id,expert_name,base_service_count,service_count from exp_expert where deleted=%s and status=%s;
"select_memberPrivilegeId":
- user
- SELECT id ,enc_user_id,privilege_type FROM uc_member_privilege WHERE left_times>0 and status=1 AND privilege_type IN (5,12,9,2,10,11) AND start_time <NOW() AND end_time >NOW() AND enc_user_id IN (SELECT enc_user_id FROM uc_user_member WHERE final_end_time >NOW() AND level_type_code=001) order by id desc limit 1
\ No newline at end of file
# 存储业务sql
"select_patient":
- medical
- SELECT relation_id,name,gender,id FROM `patient` where enc_user_id=%s and deleted=0;
# -*- encoding=utf-8 -*-
import os
import sys
import requests
import math
import re
import time
from bs4 import BeautifulSoup
from random import shuffle
import datetime
import pypinyin
import hashlib
def exchange(word):
"""
中文转拼音
:param word:
:return:
"""
s = ''
for i in pypinyin.pinyin(word, style=pypinyin.NORMAL):
s += ''.join(i)
return s
def get_message(case_path, case_name):
# 从注释中获取某个文件的 case tag 信息
tags = []
pyfile = open(case_path + os.sep + case_name + '.py', 'r', encoding='utf-8')
lines = pyfile.readlines()
for line in lines:
if 'case_tag' in line:
line = line.split('case_tag')[-1]
# 对不规范的写法做一些处理,如中文分号, 中文逗号,多空格等。。
line = re.sub(' ', '', line)
line = line.replace(',', ',').replace(':', '').replace(':', '')
line = line.strip().strip('\n')
tags = line.split(',')
pyfile.close()
return tags
def get_all_dir(case_name, case_path):
"""
:return: 获得需要执行的所有air的文件夹,同时创建单个log目录
"""
case_list = []
for dirs in os.listdir(case_path):
isdir = os.path.join(case_path, dirs)
if os.path.isdir(isdir) and isdir.endswith(".air"):
air_name = dirs.replace('.air', '')
if 'all' in case_name or air_name in case_name:
# script = os.path.join(case_path, isdir)
case_list.append(air_name)
return case_list
def get_case_tag_list(case_path, cases):
"""
遍历case目录下的文件,从文件中获取case tag,加入列表中
如果想同时满足两个条件,用#分割
"""
tagList = cases.split('#')
env = os.environ['ENV']
case_list = []
for f in os.listdir(case_path):
script = os.path.join(case_path, f)
air_name = f.replace('.air', '')
case_tags = get_message(script, air_name)
flag = False
for con in tagList:
conList = con.split(',')
if list(set(case_tags) & set(conList)):
flag = True
else:
flag = False
break
if flag and (env in case_tags or env in ('sit', 'sita')):
case_list.append(air_name)
return case_list
def get_dir_exist(path, div_num):
"""
判断报告文件夹是否存在
"""
for i in range(div_num):
test_path = path + os.sep + 'report_' + str(i+1)
if not os.path.isdir(test_path):
return False
return True
def merge_report(div_num, report_path):
"""
合并报告
"""
total_cases = 0
succ_cases = 0
max_time = 0.0
time_str = ''
summary_r = open(report_path + os.sep + 'summary.html', 'r', encoding='utf-8')
lines = summary_r.readlines()[:43]
summary_r.close()
summary_w = open(report_path + os.sep + 'summary.html', 'w', encoding='utf-8')
for i in range(div_num):
file = report_path + os.sep + 'report_' + str(i+1) + os.sep + 'summary.html'
print(file)
htmlfile = open(file, 'r', encoding='utf-8')
htmlhandle = htmlfile.read()
soup = BeautifulSoup(htmlhandle, 'html.parser')
tables = soup.find_all("table")
#print(tables)
#第一个table存的是概要,第二个table存的是具体case。先读第一个表
tab = tables[0]
tr = tab.findAll('tr')[1]
tds = tr.findAll('td')
total_cases += int(tds[0].getText())
succ_cases += int(tds[1].getText())
minite = int(tds[2].getText().split('分')[0])
second = int(tds[2].getText().split('分')[1].split('秒')[0])
number = minite + second/60
#print(number)
if number > max_time:
max_time = number
time_str = tds[2].getText()
#再读第二个表
tab = tables[1]
trs = tab.findAll('tr')
print(len(trs))
for i in range(len(trs)):
if i == 0:
continue
tds = trs[i].findAll('td')
case_name = tds[0].getText()
status = tds[1].getText()
run_time = tds[2].getText()
if status == '成功':
html = '''
<tr width="600">
<td class="details-col-elapsed"><a href='../log/%s/log.html' target='_blank'>%s</a></td>
<td class="success">%s</td>
<td class="details-col-elapsed">%s</td>
</tr>''' % (case_name, case_name, status, run_time)
else:
html = '''
<tr width="600">
<td class="details-col-elapsed"><a href='../log/%s/log.html' target='_blank'>%s</a></td>
<td class="fail">%s</td>
<td class="details-col-elapsed">%s</td>
</tr>''' % (case_name, case_name, status, run_time)
lines.append(html)
#print(html)
pass_rate = round(succ_cases*100/total_cases, 2)
summary = '''
<!--<div><h2>Test Statistics</h2></div>-->
<table width="800">
<caption><ul><li>测试概要</li></ul></caption>
<tr width="600">
<th width="300" class='details-col-msg'>用例总数</th>
<th class='details-col-msg'>成功数</th>
<th class='details-col-msg'>运行用时</th>
<th class='details-col-msg'>成功率</th>
</tr>
<tr width="600">
<td class='details-col-elapsed'>%s</td>
<td class='details-col-elapsed'>%s</td>
<td class='details-col-elapsed'>%s</td>
<td class="details-col-elapsed">%s</td>
</tr>
</table>
</table>
<!--<div><h2>Test detail</h2></div>-->
<table width="800">
<caption><ul><li>测试列表</li></ul></caption>
<tr width="600">
<th width="300" class='details-col-msg'>用例名称</th>
<th class='details-col-msg'>执行结果</th>
<th class='details-col-msg'>执行时间(秒)</th>
</tr>
''' % (total_cases, succ_cases, time_str, str(pass_rate))
summary_w.write(summary)
for line in lines:
summary_w.write(line)
end_html = '''
</table>
<div><h2></h2></div>
</div>
</body>
</html>'''
summary_w.write(end_html)
summary_w.close()
time_str = time_str.replace('分', 'm').replace('秒', 's')
return total_cases, succ_cases, time_str, pass_rate
def get_mess_from_report(report_path):
"""
从报告中获取运行case数
:return:
"""
htmlfile = open(report_path + os.sep + 'summary.html', 'r', encoding='utf-8')
htmlhandle = htmlfile.read()
soup = BeautifulSoup(htmlhandle, 'html.parser')
tables = soup.find_all("table")
# print(tables)
# 第一个table存的是概要,第二个table存的是具体case。先读第一个表
tab = tables[0]
tr = tab.findAll('tr')[1]
tds = tr.findAll('td')
total_cases = int(tds[0].getText())
succ_cases = int(tds[1].getText())
time_str = tds[2].getText()
time_str = time_str.replace('分', 'm').replace('秒', 's')
if int(total_cases) != 0:
pass_rate = round(succ_cases * 100 / total_cases, 2)
else:
pass_rate = 0.00
htmlfile.close()
return total_cases, succ_cases, time_str, pass_rate
def get(url):
try:
return requests.get(url)
except requests.exceptions.ConnectionError:
print('ConnectionError -- please wait 3 seconds')
time.sleep(1)
except requests.exceptions.ChunkedEncodingError:
print('ChunkedEncodingError -- please wait 3 seconds')
time.sleep(1)
except:
print('Unfortunitely -- An Unknow Error Happened, Please wait 3 seconds')
time.sleep(1)
def call_back_atms(runenv, job_name, build_num, total, rate, run_time, pool_name, build_user):
"""
回传数据到atms
:return:
"""
if runenv == 'autotestpreid':
runenv_b = 1
elif runenv == 'autotestsitid':
runenv_b = 2
else:
runenv_b = 0
url = "http://atms.baobaoshu.com/atms/rest/api/getJobResult?jobName=" + job_name + "&runenv=" + str(
runenv_b) + "&buildNumber=" + str(build_num) + "&allSum=" + str(total) + "&passRate=" + str(
rate) + "&execTime=" + run_time + "&poolName=" + pool_name + "&buildUser=" + build_user + "&p0Sum=0&p1Sum=0&p2Sum=0"
print(url)
response = get(url)
print("回调atms返回结果")
print(response.content.decode())
def call_back_cloud(runenv, pool_name, build_num, start, end, job_url, build_user, rate, oldrev, newrev):
"""
回传到云平台
:return:
"""
if rate == 100.0:
status = 'success'
else:
status = 'fail'
url = "http://cloud.baobaoshu.com/cloud/buildautotest?type=" + \
runenv + "&buildid=" + str(build_num) + "&pool=" + pool_name + \
"&codeversion=" + str(newrev) + "&lastcversion=" + str(oldrev) + \
"&starttime=" + str(start) + "&endtime=" + str(end) + "&status=" + status + \
"&url=" + str(job_url) + "&autopassrate=" + str(rate) + "&user=" + str(build_user)
print(url)
response = get(url)
print("回调云平台返回结果")
print(response.content.decode())
def call_back_muse(deployid, cbtoken, build_user, pass_rate, job_url, build_num):
"""
回调muse
:return:
"""
print("call back muse")
result = 'Fail'
if deployid != 'deployid' and cbtoken != 'cbtoken' and build_user == 'SYSTEM':
if pass_rate == 100.0:
result = 'Success'
url = 'http://muse.baobaoshu.com/apiv1/deployment/ci-callback/'
data = {'deployid': deployid, 'job_url': job_url, 'build_number': build_num, 'result': result, 'cbtoken': cbtoken}
r = requests.post(url, data)
print(data)
print(r.content.decode())
else:
print('非远程触发')
pass
def call_back_gitool(deployid, cbtoken, build_user, pass_rate, job_url, build_num,env_t):
"""
回调gitool
:return:
"""
print("call back gitool")
if deployid != 'deployid' and cbtoken == 'cbtoken' and build_user == 'SYSTEM':
timestamp = str(int(time.time()))
data = "gitool_autotest_api_token(- - #)" + timestamp
sign_md5 = hashlib.md5()
sign_md5.update(data.encode(encoding='utf-8'))
data = sign_md5.hexdigest()
verify_str = data + "_" + timestamp
if pass_rate == 100.0:
status = 'pass'
else:
status = 'failed'
if env_t == 'on':
url = 'http://gitool.plt.babytree-inc.com/gitool/update_online_autotest_result?'
elif env_t == 'pre':
url = 'http://gitool.plt.babytree-inc.com/gitool/update_pre_autotest_result?'
elif env_t == 'sit':
url = 'http://gitool.plt.babytree-inc.com/gitool/update_test_autotest_result?'
url = url + "branch_id="+str(deployid)+"&result="+str(status)+"&result_url="+str(job_url)+"&detail_result=&"+"verify_str="+str(verify_str)
print(url)
response = get(url)
print("回调gitool返回结果")
print(response.content.decode())
else:
print('非远程触发')
pass
if __name__ == '__main__':
start = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
print('start')
case_path = os.path.abspath(".") + os.sep + 'air_case'
report_path = os.path.abspath(".") + os.sep + 'report'
# 第一个参数,case执行类型
tag = sys.argv[1]
# 想运行多个tag 用逗号分割。 想运行同时满足两种tag的用&号连接
cases = sys.argv[2]
# 第3个参数,日志数据等级
log_level = sys.argv[3]
# 第4个参数,子任务个数,或者 是 app运行模式
num = sys.argv[4]
# 第5个参数,子任务名或者 设备uid
JobOrDevice = sys.argv[5]
# pool名
pool_name = sys.argv[6]
runenv = sys.argv[7]
job_name = sys.argv[8]
build_num = sys.argv[9]
job_url = sys.argv[10]
build_user = exchange(sys.argv[11])
oldrev = sys.argv[12]
newrev = sys.argv[13]
deployid = sys.argv[14]
cbtoken = sys.argv[15]
if runenv == 'autotestpreid':
env_t = 'pre'
elif runenv == 'autotestonlineid':
env_t = 'on'
elif runenv == 'autotestsitaid':
env_t = 'sita'
else:
env_t = 'sit'
os.environ['ENV'] = env_t
# 如果num是 s,m,d 表示是运行app 用例
if num in ('s', 'm', 'd'):
cmd = 'python runner_app.py ' + JobOrDevice + ' ' + num + ' ' + tag + ' ' + cases + ' ' + log_level
os.system(cmd)
total_cases, succ_cases, run_time, pass_rate = get_mess_from_report(report_path)
else:
num = int(num)
# 如果将任务数设置为1并且子任务名等于job名,就不分任务到子job,直接本机跑
if num != 1 or JobOrDevice != job_name:
case_list = cases.split(',')
if tag == 'tag':
case_list = get_case_tag_list(case_path, cases)
if tag == 'all':
case_list = get_all_dir(case_list, case_path)
shuffle(case_list)
length = len(case_list)
if length >= num:
div_num = num
else:
div_num = length
print(div_num)
for i in range(div_num):
cmd = 'rd /s /q report\\report_' + str(i+1)
os.system(cmd)
one_list = case_list[math.floor(i / div_num * length):math.floor((i + 1) / div_num * length)]
one_list_str = ",".join(one_list)
print(one_list_str)
url = 'http://jenkins.baobaoshu.com/view/Tools/job/' + JobOrDevice + '_' + str(i+1) + '/buildWithParameters?token=123456&tag=choose&cases=' + one_list_str + "&log_level=" + log_level + "&env=" + env_t + "&triger_job_name=" + job_name
print(url)
requests.get(url)
# 等待1分钟
time.sleep(60)
# 判断报告传输是否完成
while True:
is_done = get_dir_exist(report_path, div_num)
if is_done:
total_cases, succ_cases, run_time, pass_rate = merge_report(div_num, report_path)
break
else:
time.sleep(5)
else:
run_url = str(job_url) + str(build_num) + os.sep + "artifact" + os.sep + "report" + os.sep + "summary.html"
cmd = 'python runner.py ' + tag + ' ' + cases + ' ' + log_level + ' ' + env_t + ' ' + run_url
os.system(cmd)
total_cases, succ_cases, run_time, pass_rate = get_mess_from_report(report_path)
end = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
# 回传数据到gitool
call_back_gitool(deployid, cbtoken, build_user, pass_rate, job_url, build_num, env_t)
# 回传数据到muse
call_back_muse(deployid, cbtoken, build_user, pass_rate, job_url, build_num)
# 回传数据到atms
call_back_atms(runenv, job_name, build_num, total_cases, pass_rate, run_time, pool_name, build_user)
# 回传到云平台
call_back_cloud(runenv, pool_name, build_num, start, end, job_url, build_user, pass_rate, oldrev, newrev)
if pass_rate != 100.0:
sys.exit(1)
1691384174643 geckodriver INFO Listening on 127.0.0.1:60395
1691384177063 mozrunner::runner INFO Running command: "D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe" "--marionette" "-foreground" "-no-remote" "-profile" "C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\rust_mozprofilekzunjD"
console.warn: services.settings: Ignoring preference override of remote settings server
console.warn: services.settings: Allow by setting MOZ_REMOTE_SETTINGS_DEVTOOLS=1 in the environment
1691384177326 Marionette INFO Marionette enabled
Dynamically enable window occlusion 0
console.error: "Warning: unrecognized command line flag" "-foreground"
1691384177391 Marionette INFO Listening on port 60405
1691384177764 RemoteAgent WARN TLS certificate errors will be ignored for this session
[GFX1-]: Failed to create a D3D11 content device: 0x887a0031
[Parent 13764, IPC I/O Parent] WARNING: DuplicateHandle failed for handle 0 in TransferHandles: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:621
[GPU 15232, IPC I/O Child] WARNING: pipe error: 232: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:443
1691384189508 Marionette INFO Stopped listening on port 60405
Dynamically enable window occlusion 1
[Parent 13764, IPC I/O Parent] WARNING: DuplicateHandle failed for handle 0 in TransferHandles: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:621
[GPU 15232, IPC I/O Child] WARNING: pipe error: 232: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:443
[Parent 13764, IPC I/O Parent] WARNING: DuplicateHandle failed for handle 0 in TransferHandles: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:621
[GPU 15232, IPC I/O Child] WARNING: pipe error: 232: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:443
JavaScript error: chrome://remote/content/marionette/cert.sys.mjs, line 51: NS_ERROR_NOT_AVAILABLE: Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsICertOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData]
[Parent 13764, IPC I/O Parent] WARNING: DuplicateHandle failed for handle 0 in TransferHandles: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:621
[GPU 15232, IPC I/O Child] WARNING: pipe error: 232: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:443
[Parent 13764, IPC I/O Parent] WARNING: DuplicateHandle failed for handle 0 in TransferHandles: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:621
[GPU 15232, IPC I/O Child] WARNING: pipe error: 232: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:443
[Parent 13764, IPC I/O Parent] WARNING: DuplicateHandle failed for handle 0 in TransferHandles: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:621
[GPU 15232, IPC I/O Child] WARNING: pipe error: 232: file /builds/worker/checkouts/gecko/ipc/chromium/src/chrome/common/ipc_channel_win.cc:443
File added
/Users/zhangaizhen/devSofeware/SEARCH_Platform_Index/search-platform-index-boot/src/main/java/com/babytree/search/platform/index/controller
\ No newline at end of file
# -*- encoding=utf8 -*-
import os
import sys
import json
import ast
import yaml
curPath = os.path.abspath(os.path.dirname(__file__))
data_path = curPath + os.sep + "data"
class parseCode(object):
def getYamlValue(self, filename):
"""
获取公共sql中的sql
:param filename:
:return:
"""
f = open(filename, 'r', encoding='utf-8')
cont = f.read()
value = yaml.load(cont, Loader=yaml.Loader)
f.close()
return value
def scan_java_doc(self, path, class_name):
flag = True
controllers = []
for controller_file in os.listdir(path):
controllers.append(controller_file)
expect_file = ("".join(class_name[:1].upper() + class_name[1:]).replace("\n", "") + ".java").\
replace("(object)", "").replace(" ", "")
filename = path + os.sep + expect_file
if expect_file in controllers:
# 读取文件在后面追加方法
flag = False
return flag, filename, expect_file
def scan_java_code(self, filename, file_hash):
root_path = ""
entity_path = ""
entity_name = ""
path_flag = False
entity_flag = False
paramsKeys = []
params = {}
f = open(filename, "r", encoding="utf-8")
lines = f.readlines()
if "entity_name" in file_hash:
entity_flag = True
entity_name = file_hash["entity_name"]
for line in lines:
if "@RequestMapping" in line and "value" not in line:
root_path = line.split("\"")[1].split("\"")[0]
elif "@RequestMapping" in line and "value" in line:
path = line.split("\"")[1].split("\"")[0]
all_path = root_path + path
if all_path == file_hash["path"]:
path_flag = True
elif path_flag and "@RequestBody" in line:
boby = line.split("@RequestBody")[1].split(" ")[1].replace(" ", "")
if file_hash["entity_name"] == boby:
file_hash["other_params"] = {}
entity_new_path = entity_path.rsplit("/", 1)[0] + "/" + boby + ".java"
f = open(entity_new_path, "r", encoding="utf-8")
model_lines = f.readlines()
for model_line in model_lines:
if "private" in model_line:
params_key = model_line.split(" ")[2].replace(";", "").replace("\n", "")
params_val = model_line.split(" ")[1]
paramsKeys.append(params_key)
params[params_key] = params_val
for key, val in file_hash["params"].items():
if key not in paramsKeys:
file_hash["other_params"][key] = val
else:
if self.typeof(file_hash["params"][key]) != params[key]:
file_hash["other_params"][key] = val
if len(file_hash["other_params"]) > 0:
new_f = open(entity_new_path, 'r', encoding="utf-8")
lines = new_f.readlines()
new = []
for line in lines:
if "private" in line:
params_key = line.split(" ")[2].replace(";", "").replace("\n", "")
if params_key not in file_hash["other_params"]:
new.append(line)
elif line.replace("\n", "").replace(" ", "") == "}":
pass
else:
for k, v in file_hash["other_params"].items():
if k not in line:
new.append(line)
with open(entity_new_path, "w") as filewrite:
for var in new:
filewrite.write(var)
for k, v in file_hash["other_params"].items():
self.get_set_entity(filewrite, k, v)
filewrite.write("}")
elif entity_flag and "."+entity_name+";" in line:
entity_package = line.replace("import", "").replace(";", "").replace(" ", "")
entity_path = str(file_hash["root_path"]).split(entity_package.split(".")[0])[0] + entity_package.replace(".", "/")
def get_set_entity(self, model_file, key, val):
if isinstance(val, str):
model_file.write("private String " + key + ";\n")
key_d = "".join(key[:1].upper() + key[1:])
model_file.write("public String get" + key_d + "() { return " + key
+ ";}\n")
model_file.write("public void set" + key_d + "(String " + key +
") { this." + key + " = " + key + ";}\n")
elif isinstance(val, list):
model_file.write("private List<Object> " + key + ";\n")
key_d = "".join(key[:1].upper() + key[1:])
model_file.write("public List<Object> get" + key_d + "() { return " + key
+ ";}\n")
model_file.write("public void set" + key_d + "(List<Object> " + key +
") { this." + key + " = " + key + ";}\n")
elif isinstance(val, int):
model_file.write("private int " + key + ";\n")
key_d = "".join(key[:1].upper() + key[1:])
model_file.write("public int get" + key_d + "() { return " + key
+ ";}\n")
model_file.write("public void set" + key_d + "(int " + key +
") { this." + key + " = " + key + ";}\n")
def param_set(self, key, val, result):
if isinstance(val, str):
result = result + "@RequestParam String " + key + ","
elif isinstance(val, list):
result = result + "@RequestParam List<Object> " + key + ","
elif isinstance(val, int):
result = result + "@RequestParam int " + key + ","
return result
def typeof(self, variate):
"""
变量类型
:param variate:
:return:
"""
if isinstance(variate, int):
return "int"
elif isinstance(variate, str):
return "String"
elif isinstance(variate, float):
return "float"
elif isinstance(variate, list):
return "list"
elif isinstance(variate, tuple):
return "tuple"
elif isinstance(variate, dict):
return "dict"
elif isinstance(variate, set):
return "set"
else:
return None
def scan_auto_code(self, path, javaAddress):
file_hash = {}
dirs = os.listdir(path)
for dir in dirs:
if dir == "air_case":
air_paths = os.listdir(path + os.sep + dir)
for air_path in air_paths:
if air_path != '.DS_Store':
path = curPath + os.sep + dir + os.sep + air_path
case_paths = os.listdir(path)
for case_path in case_paths:
filename = path + os.sep + case_path + os.sep + case_path.replace(".air", ".py")
modulename = filename.split("air_case/")[1].split("/")[0]
rootpath = filename.split("air_case/")[0]
yaml_path = rootpath + "data" + os.sep + modulename + os.sep + "data"
if os.path.exists(yaml_path):
yaml_result = self.getYamlValue(yaml_path)
# 读取需要同步的接口代码
f = open(filename, "r", encoding="utf-8")
lines = f.readlines()
class_flag = False
flag_isRun = False
flag_newOrOld = False
for line in lines:
if "case_tag" in line and "change_java_code" in line:
flag_isRun = True
elif "class" in line and flag_isRun:
class_name = line.replace(":", "").replace("class", "").replace(" ", "")
flag_newOrOld, filename, expectFile = self.scan_java_doc(javaAddress, class_name)
class_flag = True
file_hash["className"] = expectFile
file_hash["root_path"] = filename.split("/controller")[0]
file_hash["package"] = "com" + filename.split("/controller")[0].split("/com")[1].replace("/", ".")
elif "def" in line and class_flag and flag_isRun:
method = line.split("(")[0].replace("def", "").replace(" ", "")
file_hash["method"] = method
elif ("payload_" in line and "commonFuc().get_business_data" in line) and class_flag and flag_isRun:
if "RequestParam" in line:
file_hash["param_type"] = "RequestParam"
elif "payload_" in line:
file_hash["param_type"] = "Entity"
file_hash["entity_name"] = line.split("=")[0].replace("payload_", "").replace(" ", "")
param_data = line.split("\"")[1].split("\"")[0]
file_hash["params"] = yaml_result[param_data]
elif "_url" in line and class_flag and flag_isRun:
url_data = line.split("+")[1].split("\"")[1].split("\"")[0]
file_hash["path"] = yaml_result[url_data]
elif "commonFuc().http_" in line and class_flag and flag_isRun:
post_get = line.split("http_")[1].split("(")[0]
file_hash["methodPath"] = "@RequestMapping(value = \"" + file_hash["path"] + "\", method = RequestMethod." + post_get.upper() + ")"
elif "checkDict_" in line and "check_result" not in line and flag_isRun:
file_hash["response"] = line.split("=")[0].replace("checkDict_", "").replace(" ", "")
if flag_isRun:
if flag_newOrOld:
file = open(filename, 'w')
file.write("package " + file_hash["package"] + ".controller;\n\n\n")
file.write("@RestController\n")
file.write("public class " + file_hash["className"].replace(".java", "") + " {" + "\n")
file.write("\n\n")
file.write(file_hash["methodPath"])
file.write("\n@ResponseBody \n")
if "params" in file_hash:
if file_hash["param_type"] == "RequestParam":
param_body = ""
for key, val in file_hash["params"].items():
param_body = self.param_set(key, val, param_body)
file.write("public " + file_hash["response"] + " " + file_hash["method"] + "(" + param_body[:-1] + ") {\n")
else:
entity_name = file_hash["entity_name"]
param_body = "@RequestBody " + entity_name + " " + str.lower(entity_name)
file.write("public " + file_hash["response"] + " " + file_hash["method"] + "("
+ param_body + ") {\n")
model_path = file_hash["root_path"] + "/model"
if not os.path.exists(model_path):
os.mkdir(model_path)
model_filename = model_path + os.sep + entity_name + ".java"
if os.path.exists(model_filename):
os.remove(model_filename)
model_file = open(model_filename, 'a')
model_file.write("package " + file_hash["package"] + ".model;\n\n\n")
model_file.write("public class " + entity_name + "{\n\n")
for key, val in file_hash["params"].items():
self.get_set_entity(model_file, key, val)
model_file.write("}\n")
file.write("\n return null; \n")
file.write(" }\n")
file.write("}\n")
else:
self.scan_java_code(filename, file_hash)
if __name__ == '__main__':
# 第一个参数 本地java代码地址
#pool = sys.argv[1]
f = open(curPath + os.sep + 'javaAddressPath.txt', "r", encoding="utf-8")
lines = f.readlines()
javaAddress = lines[0].replace("\n", "").replace(" ", "")
parseCode().scan_auto_code(curPath, javaAddress)
查询赠品 1695341383430 1695341383724 liguangyu
查询失效商品 1695341384600 1695341384617 liguangyu
查询控销商品 1695341384633 1695341384651 liguangyu
查询已上架商品 1695341384667 1695341384689 liguangyu
需求单_获取用户信息失败 1695341384705 1695341385259 liguangyu
需求单-草稿订单删除 1695341385277 1695341387469 liguangyu
获取需求单列表 1695341387487 1695341389446 liguangyu
需求单-草稿订单删除-逆向 1695341389468 1695341391342 liguangyu
需求单删除 1695341391362 1695341392901 liguangyu
重复删除需求单 1695341392919 1695341394639 liguangyu
删除其他状态下需求单 1695341394659 1695341396211 liguangyu
删除不存在的需求单 1695341396229 1695341397377 liguangyu
需求单审核通过_代客下单 1695341397397 1695341404105 liguangyu
需求单审核通过_快速下单 1695341404119 1695341412518 liguangyu
需求单创建 1695341412538 1695341414487 liguangyu
需求草稿订单创建 1695341414505 1695341416671 liguangyu
新增赠品_控销_商品至常购清单列表 1695341416690 1695341418596 liguangyu
新增不存在的商品至常购清单列表 1695341418615 1695341419290 liguangyu
新增赠品至常购清单列表 1695341419306 1695341421315 liguangyu
获取用户常购清单列表 1695341421332 1695341425024 liguangyu
常购清单列表商品移除 1695341425048 1695341428888 liguangyu
新增JDE已停用的商品至常购清单列表 1695341428904 1695341430797 liguangyu
新增控销商品至常购清单列表 1695341430813 1695341432656 liguangyu
添加跨站点商品至常购清单列表 1695341432673 1695341433626 liguangyu
新增商品至常购清单列表 1695341433646 1695341435762 liguangyu
新增已失效商品至常购清单列表 1695341435783 1695341437875 liguangyu
客户查询 1695341437892 1695341439033 liguangyu
审核不存在的需求单 1695341439053 1695341440000 liguangyu
重复审核 1695341440016 1695341442815 liguangyu
审核其他状态的需求单 1695341442838 1695341444398 liguangyu
需求单审核不通过 1695341444418 1695341445828 liguangyu
需求单创建 1695341445842 1695341448060 liguangyu
需求单删除操作 1695341448077 1695341449480 liguangyu
获取需求单列表 1695341449498 1695341454095 liguangyu
需求单取消操作 1695341454118 1695341455477 liguangyu
新增跨站点商品至购物车 1695341455494 1695341457125 liguangyu
新增已控销商品至购物车列表 1695341457144 1695341459274 liguangyu
购物车新增商品 1695341459290 1695341461465 liguangyu
添加JDE已停用的商品至购物车列表 1695341461484 1695341463728 liguangyu
购物车数量修改验证 1695341463749 1695341466697 liguangyu
购物车商品移除 1695341466715 1695341468735 liguangyu
新增赠品至购物车 1695341468755 1695341469929 liguangyu
新增不存在商品至购物车列表 1695341469952 1695341470828 liguangyu
获取购物车列表 1695341470846 1695341472525 liguangyu
新增已失效商品至购物车列表 1695341472545 1695341474619 liguangyu
新增赠品_控销_商品至购物车 1695341474635 1695341476709 liguangyu
用户信息查询 1695341476728 1695341477685 liguangyu
商品列表_快速下单 1695341477706 1695341479429 liguangyu
商品列表_代客下单 1695341479449 1695341481128 liguangyu
获取客户列表 1695341481149 1695341482426 liguangyu
采购单查询 1695341482446 1695341483593 liguangyu
快速下单权限_否 1695341483614 1695341484605 liguangyu
快速下单权限_能 1695341484623 1695341485617 liguangyu
获取站点信息 1695341485636 1695341486392 liguangyu
站点切换 1695341486412 1695341487156 liguangyu
项目结构:
autotest-airtest-web:
---common 存放公共方法
---air_case 存放用例case
---templates 存在模板报告
---report 存放case执行总报告及各用例报告
---log 存放case执行日志
---data 存放数据
---runner.py case执行调度入口
---distribute.py 任务分发及上传数据到云平台
不方便准备环境的童鞋可以直接登录云编辑器,在云编辑器上直接开发用例,操作手册链接:http://space.babytree-inc.com/pages/viewpage.action?pageId=27535942
如果有权限问题,请联系caiyubing解决
一、环境准备:
1、python包安装:
要求: python要python3以上,pip版本要在20以上。使用python -V和pip -V查看各自版本
将python安装环境配置到环境变量中
详细安装文档参考http://space.babytree-inc.com/pages/viewpage.action?pageId=24940086,编写用例手册查看http://space.babytree-inc.com/pages/viewpage.action?pageId=24940840
2、安装chromedriver,后面跑web测试需要用到。
直接下载chromedriver并添加路径
http://chromedriver.storage.googleapis.com/index.html
下载和chrome及电脑系统对应版本的chromedriver
--mac添加到路径的方式是:
进入下载文件所在位置
unzip chromedriver_mac64.zip
mv chromedriver /usr/local/bin
--windows添加到路径的方式是
Windows 进入系统->高级设置->环境变量进行设置->选中path,点编辑,把chrome浏览器的路径(比如:C:\Users\Administrator\AppData\Local\Google\Chrome\Application)和chromedriver的路径加到path里。
二、case编写
可根据不同的情况选择拷贝不同的demo脚本。
本地执行dubbo拷贝【dubbo-local-demo.air】文件
openapi拷贝【openapi-demo.air】文件
mapi get请求拷贝【httpget-demo.air】文件
mapi post请求拷贝【httppost-demo.air】文件
注意:
1. 拷贝后修改文件名.air的文件名和.py的文件名保持一致
2. 请注意下方case_tag中新增pool名、taskCode、执行环境(sit/on/pre)等其他需要增加的tag
三、case 执行:
执行某个tag的case并设置日志等级为DEBUG, tag在每个用例上的case_tag的后面取:
python runner.py tag 100086 DEBUG sit
执行case中的某个场景命令:
python runner.py sc 月嫂 babyCareSearch 传参sortType=1 DEBUG sit
说明: 月嫂 模块名 babyCareSearch(.air文件夹的名字) 传参sortType=1(场景的名字,支持模糊匹配,但是不能有空格)
多个casetag用英文逗号分割。 如果需要同时满足多种tag用#分割
如python runner.py tag api#user-web DEBUG sit 表示执行同时满足api 且 user-web两个条件的case
python runner.py tag api#user-web,health-web 表示执行满足api 且 (user-web或者health-web)条件的case
DEBUG表示设置日志等级,可选参数有
DEBUG,INFO,WARN
sit表示选择执行环境。可输入参数sit,on,pre
sit表示测试环境,on 表示线上环境, pre预生产环境
具体case能否在各环境执行视case情况而定
四、任务分发
如果本地调试用二中的方式调用就行。
如果需要快速执行完任务,把任务分发的几个不同的子job去执行就采用以下调度方式。子job需要自己创建
python distribute.py %tag% %cases% %log_level% %num% %JobOrDevice% %pool_name% %run_env% %JOB_NAME% %BUILD_NUMBER% %JOB_URL% %BUILD_USER% %oldrev% %newrev% %deployid% %cbtoken%
如果不需要分发任务到其他子job,num传参数1,JobOrDevice传本job名
#重要说明
自动化case在sit,pre,on三个环境下执行。sit环境可以直接通过命令连接数据库。而pre,on环境需要从http://idb.baobaoshu.com/ 里去查询。
我们封装的查询方法在 common/db/sql/sql.py里。登录账号及选择数据库,环境,服务器需要各业务线根据自己的需求去自主填写。
即在common/db/sql/sql.py 里找到以下内容,填入对应值。
driver.find_element_by_id('username').send_keys()
driver.find_element_by_name('password').send_keys()
#选择数据库,,括号里填入自己数据库的值
Select(driver.find_element_by_id("database")).select_by_value()
#选择环境
Select(driver.find_element_by_id("environment")).select_by_value()
#选择服务器
Select(driver.find_element_by_id("server")).select_by_value()
五、功能介绍
> 支持美囤和北京数据库测试环境线上环境查询、美囤测试环境update和delete
例子:
美囤调用: result_db = dbOP().selectSql('exp_expert', [0, 20])
北京调用: result_db = dbOP().selectSql('exp_expert', [0, 20], 'bj_db')
六、 如何进行代码review
> 代码提交到gerrit后,请通知团队的测试进行review
> review结果请写入到reply中
若用例中掉了场景,则需要填写;用例XXXX(填上.air名)差场景:XXX、场景:XXX,并通知开发修改
若case没有问题,则直接写review通过
>
.hljs{display:block;padding:.5em;background:#23241f}.hljs,.hljs-tag,.css .hljs-rules,.css .hljs-value,.css .hljs-function .hljs-preprocessor,.hljs-pragma{color:#f8f8f2}.hljs-strongemphasis,.hljs-strong,.hljs-emphasis{color:#a8a8a2}.hljs-bullet,.hljs-blockquote,.hljs-horizontal_rule,.hljs-number,.hljs-regexp,.alias .hljs-keyword,.hljs-literal,.hljs-hexcolor{color:#ae81ff}.hljs-tag .hljs-value,.hljs-code,.hljs-title,.css .hljs-class,.hljs-class .hljs-title:last-child{color:#a6e22e}.hljs-link_url{font-size:80%}.hljs-strong,.hljs-strongemphasis{font-weight:bold}.hljs-emphasis,.hljs-strongemphasis,.hljs-class .hljs-title:last-child{font-style:italic}.hljs-keyword,.hljs-function,.hljs-change,.hljs-winutils,.hljs-flow,.lisp .hljs-title,.clojure .hljs-built_in,.nginx .hljs-title,.tex .hljs-special,.hljs-header,.hljs-attribute,.hljs-symbol,.hljs-symbol .hljs-string,.hljs-tag .hljs-title,.hljs-value,.alias .hljs-keyword:first-child,.css .hljs-tag,.css .unit,.css .hljs-important{color:#f92672}.hljs-function .hljs-keyword,.hljs-class .hljs-keyword:first-child,.hljs-constant,.css .hljs-attribute{color:#66d9ef}.hljs-variable,.hljs-params,.hljs-class .hljs-title{color:#f8f8f2}.hljs-string,.css .hljs-id,.hljs-subst,.haskell .hljs-type,.ruby .hljs-class .hljs-parent,.hljs-built_in,.sql .hljs-aggregate,.django .hljs-template_tag,.django .hljs-variable,.smalltalk .hljs-class,.django .hljs-filter .hljs-argument,.smalltalk .hljs-localvars,.smalltalk .hljs-array,.hljs-attr_selector,.hljs-pseudo,.hljs-addition,.hljs-stream,.hljs-envvar,.apache .hljs-tag,.apache .hljs-cbracket,.tex .hljs-command,.hljs-prompt,.hljs-link_label,.hljs-link_url{color:#e6db74}.hljs-comment,.hljs-javadoc,.java .hljs-annotation,.python .hljs-decorator,.hljs-template_comment,.hljs-pi,.hljs-doctype,.hljs-deletion,.hljs-shebang,.apache .hljs-sqbracket,.tex .hljs-formula{color:#75715e}.coffeescript .javascript,.javascript .xml,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata,.xml .php,.php .xml{opacity:.5}
\ No newline at end of file
@charset "utf-8";
* {
padding: 0;
margin: 0;
}
html{
height: 100%;
}
body{
background: linear-gradient(#0A0011, #0C0B1F, #101628, #151D2F, #11182A, #150D21, #0A0011);
color: rgb(237,237,237);
font-size: 14px;
height: 100%;
position: relative;
}
.container-fluid{
margin: auto;
padding-right: 100px;
padding-left: 100px;
margin-bottom: 220px;
min-width: 960px;
min-height: calc(100% - 222px);
position: relative;
}
#back_multi .back {
position: fixed;
left: 20px;
top: 50px;
width: 50px;
height: 50px;
display: block;
border: solid 1px #2f4682;
border-radius: 40px;
}
#back_multi .back:hover {
background: #223159;
}
#back_multi .back img {
width: 25px;
margin-top: 12px;
margin-left: 12px;
}
h2.empty-report{
text-align: center;
margin-bottom: 60px;
margin-top: 30px;
}
.title{
font-size: 32px;
text-align: center;
padding-top: 50px;
}
.info-title{
font-size: 20px;
margin-bottom: 20px
}
.summary{
margin: 30px 0;
background: #121c34;
padding-top: 30px;
padding-left: 30px;
}
.summary .show-vertical, .summary .info3, .summary .show-horizontal .info{
width: 48%;
display: inline-block
}
.summary .show-horizontal .info2{
/* border-left: dashed 1px rgb(102,102,102); */
}
.summary .info{
line-height: 18px;
}
.summary .info3{
/* border-left: dashed 1px rgb(102,102,102); */
padding-left: 50px;
min-height: 260px;
vertical-align: top;
width: calc(48% - 50px);
}
.summary .info-left, .summary .info-right{
display: inline-block;
}
.summary .info-left{
width: 60px;
height: 100%;
vertical-align: top;
}
.summary .info-right {
width: calc(100% - 80px);
}
.summary .info-title .green, .summary .info-title .red{
color: rgb(184,233,134);
font-size: 16px;
margin-left: 48px;
display: inline-block;
}
.summary .info-title .red{
color: #fd5a3e;
}
.summary .info-content .info-sub{
display: inline-block;
margin-right: 30px;
}
.summary .info-toal{
margin: 18px 0 35px 0
}
.summary .info-toal>div{
display: inline-block;
height: 30px;
min-width: 30%;
}
.summary .info-toal .info-step, .summary .info-toal .info-console{
margin-right: 100px;
}
.summary .info-toal .info-console img#show-console {
width: 25px;
margin-left: 10px;
vertical-align: middle;
}
.summary .info-toal .info-console img#show-console:hover{
content: url(../image/console_hover.svg);
}
.summary .info-toal .info-value{
color: rgb(74,144,226);
font-size: 18px;
padding-left: 5px;
}
.summary .info-toal .info-value a{
color: rgb(74,144,226);
}
.summary .info-toal .info-value img{
margin-left: 10px;
width: 15px;
}
.summary .info-execute{
line-height: 18px;
margin: 20px 0;
cursor: context-menu;
position: relative;
}
.summary .info-execute #copy_path{
display: inline-block;
margin-left: 10px;
width: 20px;
vertical-align: bottom;
cursor: context-menu;
opacity: 0.6;
}
.summary .info-execute #copy_path:hover{
opacity: 1;
}
.summary .circle-img{
background: rgb(216,216,216);
width: 18px;
height: 18px;
float: left;
border-radius: 10px;
margin-right: 10px;
}
.summary .info-execute .info-name{
line-height: 18px;
}
.summary .airdesc.long .show-more{
content: '';
height: 25px;
width: 25px;
display: block;
background: url(../image/less.svg) no-repeat;
background-size: contain;
margin-left: 50px;
opacity: 0.5;
position: absolute;
}
.summary .airdesc.collapse .show-more{
display: block;
transform: rotate(180deg);
}
.summary .airdesc {
padding-bottom: 30px;
position: relative;
}
.summary .airdesc .desc-content{
height: auto;
overflow-y: hidden;
line-height: 24px;
font-size: 12px;
white-space: pre-wrap;
word-break: break-word;
}
.summary .airdesc.collapse .desc-content{
height: 160px;
}
.gallery{
margin-bottom: 30px;
}
.gallery .info-title{
margin-bottom: 10px;
}
.gallery .content{
background: rgb(18,28,52);
overflow-x: scroll;
white-space: nowrap;
background: #121c34;
padding-top: 20px;
}
.gallery .thumbnail {
display: inline-block;
border: solid 1px transparent;
}
.gallery .thumbnail.active {
border: solid 1px rgb(74,144,226);
}
.gallery .thumbnail img{
max-width: 120px;
max-height: 130px;
}
.gallery .thumbnail .time{
text-align: center;
font-size: 12px;
margin: 3px 0;
}
.step-list .no-steps{
text-align: center;
}
.step-list .content{
background: rgb(18,28,52)
}
#device{
display: none;
flex-wrap: wrap;
-ms-flex-wrap: wrap;
padding: 40px 0 30px 80px;
background: #121c34;
margin-bottom: 49px;
}
#device.show{
display: -ms-flexbox;
display: flex;
justify-content: flex-start;
}
#device .info{
width: 48%;
line-height: 30px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#device .info.connect{
position: relative;
padding-right: 30px;
}
#device .info .copy_device{
content: "";
width: 18px;
height: 18px;
background: url(../image/copy.svg) no-repeat;
background-size: contain;
position: absolute;
right: 4px;
top: 3px;
opacity: 0.6;
}
#device .info .copy_device:hover{
opacity: 1;
}
#device .info span:first-child{
margin-right: 10px;
}
.steps-head {
position: relative;
background: rgb(22,34,62);
line-height: 40px;
}
.head-left {
padding-left: 20px;
}
.steps-head .order{
width: 100px;
text-align: center;
display: inline-block;
cursor: context-menu;
position: relative;
}
.steps-head .order::after{
content: "";
width: 16px;
height: 16px;
display: inline-block;
margin-left: 7px;
vertical-align: middle;
}
.steps-head .order:hover::after{
background: url(../image/order.svg);
}
.head-right{
position: absolute;
top: 0;
right: 0;
}
.head-right .filters{
margin-right: 20px;
margin-left: 10px;
}
.head-right .filter{
margin: 0 3px;
}
.head-right .jump-wrong {
margin-right: 40px;
}
.head-right .jump-wrong, .head-right .filter{
padding: 0 10px;
background: #1F3057;
cursor: context-menu;
}
.head-right .jump-wrong:hover, .head-right .filter:hover{
background: #283D6F
}
.head-right .filter.active{
background: #8B632A;
}
.steps-content {
background: rgb(18,28,52);
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: -moz-box;
display: -moz-flex;
display: flex;
-webkit-box-align: stretch;
-webkit-align-items: stretch;
-moz-box-align: stretch;
-moz-align-items: stretch;
-ms-flex-align: stretch;
align-items: stretch;
}
.step-left{
min-width: 320px;
padding: 30px 20px;
width: 32%;
max-width: 500px;
}
.step-right{
border-left: dashed 1px rgb(102,102,102);
padding: 30px 0px 0 60px;
margin-left: 30px;
vertical-align: top;
min-height: 500px;
width: 100%;
max-width: calc(100% - 410px);
max-width: calc(100% - 600px);
}
.step-left .step {
line-height: 36px;
position: relative;
cursor: context-menu;
padding: 0 10px;
}
.step-left .step:hover {
background: rgb(22,34,62)
}
.step-left .step.active{
background: #040F26
}
.step-left .step>img{
vertical-align: middle;
}
.step-left .step .step-context {
position: absolute;
width: 20px;
top: 10px;
right: -25px;
cursor: context-menu;
opacity: 0.6;
}
.step-left .step .step-context:hover{
opacity: 1;
}
.step-left .order{
margin: 0 5px;
}
.step-left .step_title {
word-break: break-all;
}
.step-left .step-time{
position: absolute;
right: 10px;
}
.step-right .step-head{
font-size: 24px;
font-weight: bold;
margin-bottom: 15px;
word-break: break-all;
}
.step-right .step-status{
margin-right: 20px;
border-radius: 5px;
font-size: 18px;
font-weight: bolder;
padding: 3px 7px;
}
.step-right .step-status.success{
background: #417505;
}
.step-right .step-status.fail{
background: #fd5a3e
}
.step-right .step-infos{
line-height: 30px;
}
.step-right .step-infos .content-val.success {
color: #97cc64;
}
.step-right .step-infos .content-val.fail {
color: #fd5a3e;
}
.step-right .step-infos img{
vertical-align: middle;
margin: 0 5px;
}
.step-right .bold{
font-size: 18px;
font-weight: bold;
}
.step-right .step-args {
width: 100%;
border-top: solid 1px #212f53;
margin-top: 20px;
padding-top: 30px;
padding-bottom: 50px;
}
.step-right .step-args .crop_image {
margin-top: 7px;
max-width: calc(100% - 80px);
max-height: 100%;
}
.step-right .step-args .desc{
margin-left: 50px;
}
.step-right .fancybox{
margin: auto;
position: relative;
max-width: 100%;
}
.step-right .step-args .fluid {
display: inline-block;
vertical-align: top;
margin-bottom: 20px;
}
.fluid.infos{
min-width: 300px;
}
.step-right .fluid.screens, .step-right .fluid.traces{
width: 100%;
max-width: calc(100% - 330px);
min-width: 500px;
}
.fancybox .screen{
max-width: 100%;
border: solid 1px #212f53;
max-height: 600px;
}
.fancybox .target{
position: absolute;
width: 50px;
height: 50px;
animation-name: rubberBand;
-webkit-animation-name: rubberBand; /* Safari 和 Chrome */
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.fancybox .rect {
position: absolute;
border: solid 2px red;
border-radius: 3px;
background: transparent;
}
.fancybox .arrow {
display: flex;
align-items: center;
left: 100px;
top: 100px;
position: absolute;
width: 120px;
margin-top: -15px;
margin-left: -6px;
}
.fancybox .arrow .start {
width: 12px;
height: 12px;
border-radius: 6px;
background-color: red;
}
.fancybox .arrow .line {
flex: 1;
background: red;
height: 10px;
}
.fancybox .arrow .end {
width: 0;
height: 0;
border-top: 15px solid transparent;
border-bottom: 15px solid transparent;
border-left: 20px solid red;
}
pre.trace {
white-space: pre-wrap;
word-break: break-word;
}
#pageTool {
margin-top: 50px;
position: relative;
height: 50px;
}
#pageTool .js-page-first, #pageTool .js-page-last{
display: none;
}
#pageTool ul{
padding-left: 0;
}
#pageTool .ui-paging-container li{
margin-bottom: 10px;
}
#pageTool .ui-pager-disabled{
color: rgb(59, 81, 134);
border: solid 1px #233868;
background: transparent;
}
#pageTool .focus, #pageTool li.ui-pager:hover {
color:rgb(74,144,226);
background: transparent;
border: solid 1px rgb(74,144,226);
}
#pageTool .steps-total {
display: block;
margin: auto;
text-align: center;
color: rgb(74,144,226);
font-size: 12px;
position: absolute;
top: 4px;
right: -14px;
}
#pageTool .ui-paging-container{
position: absolute;
right: 50px;
top: 0px;
}
#pageTool .ui-select-pagesize, #pageTool .ui-paging-count{
background: transparent;
color: rgb(74,144,226);
}
.row.gif-wrap {
position: fixed;
bottom: 0;
right: 0;
}
.row.gif-wrap .menu{
display: block;
text-align: right;
background: rgba(0, 0, 0, 0.7);
min-width: 100px;
height: 38px;
}
.row.gif-wrap .pattern1, .row.gif-wrap .col-md-6{
display: none;
}
.row.gif-wrap.show .pattern1, .row.gif-wrap.show .col-md-6{
display: block;
}
.row.gif-wrap .pattern2{
display: block;
}
.row.gif-wrap.show .pattern2{
display: none;
}
.row.gif-wrap .pattern {
margin-right: 14px;
}
.row.gif-wrap .pattern>div{
display: inline-block;
padding: 8px;
width: 20px;
height: 20px;
margin-left: 2px;
}
.row.gif-wrap .pattern .minimize {
width: 18px;
}
.row.gif-wrap .embed-responsive{
display: inline-block;
position: relative;
}
.row.gif-wrap .embed-responsive .open_in_new_tab{
position: absolute;
top: -34px;
right: 100px;
transition: all 0.3s ease-out;
background: rgba(0, 0, 0, 0.7);
padding: 8px;
border-radius: 44px;
width: 16px;
height: 15px;
z-index: 2;
}
.row.gif-wrap img{
max-width: 100%;
max-height: 100%;
opacity: 0.6;
}
.row.gif-wrap img:hover{
opacity: 1;
transform: scale(1.1);
}
.row.gif-wrap .embed-responsive-item {
max-width: 500px;
max-height: 500px;
}
.mask {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.2);
z-index: 2;
display: none;
}
.mask .content {
width: 80%;
height: calc(100% - 140px);
margin: auto;
margin-top: 60px;
padding-top: 40px;
position: relative;
}
.mask .content .console-content{
height: 100%;
overflow-y: auto;
}
#magnify.mask{
background: rgba(255, 255, 255, 0.7);
}
#magnify .content{
padding: 0;
height: calc(100% - 100px);
display:flex;
justify-content: center;
align-items: center;
}
#magnify .fancybox, #magnify .crop_image{
display:block;
max-width:100%;
max-height:100%;
position: relative;
}
#magnify .fancybox .screen{
height: auto;
max-height: 100%;
margin: auto;
display: block;
}
#close-console {
position: absolute;
top: 7px;
right: 10px;
width: 25px;
border: solid 1px white;
border-radius: 30px;
}
#close-console:hover {
background: rgba(255, 255, 255, 0.3);
}
.footer {
height: 120px;
color: #999;
min-width: 1160px;
border-top: solid 1px #172036;
position: absolute;
width: 100%;
bottom: -219px;
left: 0;
}
.footer-content {
display: flex;
align-items: center;
justify-content: center;
padding: 34px 0;
}
.foo {
min-width: 300px;
width: 20%;
}
.foo:last-child {
min-width: 455px;
font-size: 14px;
}
.footer a{
text-decoration: none;
display: block;
color: inherit;
}
.footer .foo >div {
display: inline-block;
vertical-align: middle;
}
.footer .interfaces .icon:first-child{
border: none;
}
.footer .interfaces .icon{
margin: 15px 0;
display: inline-block;
padding: 0 16px;
border-left: solid 1px #666666;
}
.footer .icon img{
vertical-align: middle;
}
.footer .apps .icon {
display: inline-block;
line-height: 40px;
margin-right: 30px;
}
.footer .corp img{
width: 105px;
vertical-align: middle;
margin-right: 20px;
}
/* animation */
@-webkit-keyframes rubberBand {
from {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
30% {
-webkit-transform: scale3d(1.25, 0.75, 1);
transform: scale3d(1.25, 0.75, 1);
}
40% {
-webkit-transform: scale3d(0.75, 1.25, 1);
transform: scale3d(0.75, 1.25, 1);
}
50% {
-webkit-transform: scale3d(1.15, 0.85, 1);
transform: scale3d(1.15, 0.85, 1);
}
65% {
-webkit-transform: scale3d(.95, 1.05, 1);
transform: scale3d(.95, 1.05, 1);
}
75% {
-webkit-transform: scale3d(1.05, .95, 1);
transform: scale3d(1.05, .95, 1);
}
to {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
@keyframes down {
0% { bottom: 10px; }
50% {bottom: 3px;}
100% { bottom: 10px; }
}
@-moz-keyframes down /* Firefox */{
0% { bottom: 10px; }
50% {bottom: 3px;}
100% { bottom: 10px; }
}
@-webkit-keyframes down /* Safari 和 Chrome */{
0% { bottom: 10px; }
50% {bottom: 3px;}
100% { bottom: 10px; }
}
@-o-keyframes down /* Opera */{
0% { bottom: 10px; }
50% {bottom: 3px;}
100% { bottom: 10px; }
}
/* 滚动条 */
::-webkit-scrollbar {
width: 6px; /*滚动条的宽度*/
height: 6px; /*滚动条的高度*/
}
::-webkit-scrollbar-thumb:horizontal { /*水平滚动条的样式*/
background-color: #324163;
-webkit-border-radius: 6px;
outline: 2px solid #fff;
}
::-webkit-scrollbar-track-piece {
background-color: rgb(10, 10, 28); /*滚动条的背景颜色*/
-webkit-border-radius: 0; /*滚动条的圆角宽度*/
}
::-webkit-scrollbar-thumb:vertical { /*垂直滚动条的样式*/
height: 50px;
background-color: #324163;
-webkit-border-radius: 4px;
outline: 2px solid #fff;
outline-offset: -2px;
border: 1px solid #324163;
}
::-webkit-scrollbar-thumb:hover { /*滚动条的hover样式*/
height: 50px;
background-color: #9f9f9f;
-webkit-border-radius: 4px;
}
/* 分页 */
.ui-paging-container{color:#666;font-size:12px}.ui-paging-container ul{overflow:hidden;text-align:center}.ui-paging-container li,.ui-paging-container ul{list-style:none}.ui-paging-container li{display:inline-block;padding:3px 6px;margin-left:5px;color:#666}.ui-paging-container li.ui-pager{cursor:pointer;border:1px solid #ddd;border-radius:2px}.ui-paging-container li.focus,.ui-paging-container li.ui-pager:hover{background-color:#288df0;color:#FFF}.ui-paging-container li.ui-paging-ellipse{border:none}.ui-paging-container li.ui-paging-toolbar{padding:0}.ui-paging-container li.ui-paging-toolbar select{height:22px;border:1px solid #ddd;color:#666}.ui-paging-container li.ui-paging-toolbar input{vertical-align:top;line-height:20px;height:20px;padding:0;border:1px solid #ddd;text-align:center;width:30px;margin:0 0 0 5px}.ui-paging-container li.ui-paging-toolbar a{text-decoration:none;display:inline-block;height:20px;border:1px solid #ddd;vertical-align:top;border-radius:2px;line-height:20px;padding:0 3px;cursor:pointer;margin-left:5px;color:#666}.ui-paging-container li.ui-pager-disabled,.ui-paging-container li.ui-pager-disabled:hover{background-color:#f6f6f6;cursor:default;border:none;color:#ddd}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment