├── .gitignore ├── Admin ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── admin.cpython-36.pyc │ ├── admin.cpython-37.pyc │ ├── models.cpython-36.pyc │ ├── models.cpython-37.pyc │ ├── views.cpython-36.pyc │ └── views.cpython-37.pyc ├── admin.py ├── apps.py ├── migrations │ ├── __init__.py │ └── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ └── __init__.cpython-37.pyc ├── models.py ├── tests.py └── views.py ├── Autotest_platform ├── PageObject │ ├── Base.py │ ├── Base_m.py │ ├── Gift.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── Base.cpython-36.pyc │ │ ├── Base.cpython-37.pyc │ │ ├── Base_m.cpython-36.pyc │ │ ├── __init__.cpython-36.pyc │ │ └── __init__.cpython-37.pyc │ └── logger.py ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── celery.cpython-36.pyc │ ├── celery.cpython-37.pyc │ ├── settings.cpython-36.pyc │ ├── settings.cpython-37.pyc │ ├── urls.cpython-36.pyc │ ├── urls.cpython-37.pyc │ ├── wsgi.cpython-36.pyc │ └── wsgi.cpython-37.pyc ├── celery.py ├── helper │ ├── Http.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── Http.cpython-36.pyc │ │ ├── Http.cpython-37.pyc │ │ ├── __init__.cpython-36.pyc │ │ ├── __init__.cpython-37.pyc │ │ ├── util.cpython-36.pyc │ │ └── util.cpython-37.pyc │ └── util.py ├── settings.py ├── urls.py └── wsgi.py ├── Product ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── admin.cpython-36.pyc │ ├── admin.cpython-37.pyc │ ├── models.cpython-36.pyc │ ├── models.cpython-37.pyc │ ├── tasks.cpython-36.pyc │ ├── tasks.cpython-37.pyc │ ├── views.cpython-36.pyc │ └── views.cpython-37.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_delete_user.py │ ├── 0003_remove_project_creator.py │ ├── 0004_project_creator.py │ ├── 0005_auto_20190627_1542.py │ ├── 0006_loginconfig_logintype.py │ ├── 0007_environmentlogin_logintype.py │ ├── 0008_auto_20190702_1507.py │ ├── 0009_auto_20190719_1135.py │ ├── 0010_auto_20190722_1136.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-36.pyc │ │ ├── 0001_initial.cpython-37.pyc │ │ ├── 0002_delete_user.cpython-36.pyc │ │ ├── 0003_remove_project_creator.cpython-36.pyc │ │ ├── 0003_user.cpython-36.pyc │ │ ├── 0004_project_creator.cpython-36.pyc │ │ ├── 0005_auto_20190627_1542.cpython-36.pyc │ │ ├── 0006_loginconfig_logintype.cpython-36.pyc │ │ ├── 0007_environmentlogin_logintype.cpython-36.pyc │ │ ├── 0008_auto_20190702_1507.cpython-36.pyc │ │ ├── 0009_auto_20190719_1135.cpython-36.pyc │ │ ├── 0010_auto_20190722_1136.cpython-36.pyc │ │ ├── __init__.cpython-36.pyc │ │ └── __init__.cpython-37.pyc ├── models.py ├── tasks.py ├── tests.py └── views.py ├── README.md ├── __pycache__ ├── manage.cpython-36.pyc └── manage.cpython-37.pyc ├── autotest.sql ├── docker-compose.yml ├── manage.py ├── pictures ├── assertValue.png ├── checkElementParameters.png ├── codeDiff.png ├── copyTesecase.png ├── djangoSettingDatabase.png ├── djangoUsersManager.png ├── dockerMysql.png ├── dockerSharedDrives.png ├── elementManager.png ├── improvedVersion.png ├── index.png ├── initDatabase.png ├── installPyRequirements.png ├── linkDockerMysql.png ├── linuxCelery.png ├── loginPage.png ├── loginSetting.png ├── pageManager.png ├── projectManager.png ├── runCelery.png ├── runSqlFile.png ├── runSqlFileSetting.png ├── selectAutotestDatabase.png ├── seleniumManager.png ├── setSelenium.png ├── testReport.png └── timeSetting.png ├── pip.conf ├── requirements.txt ├── static └── assets │ ├── css │ ├── bootstrap.css │ ├── custom-styles.css │ └── font-awesome.css │ ├── datetimepicker │ ├── bootstrap-datetimepicker.min.css │ ├── bootstrap-datetimepicker.min.js │ ├── bootstrap-datetimepicker.zh-CN.js │ └── moment-with-locales(1).js │ ├── font-awesome │ └── fonts │ │ ├── fontawesome-webfontba72.eot │ │ ├── fontawesome-webfontba72.svg │ │ ├── fontawesome-webfontba72.ttf │ │ ├── fontawesome-webfontba72.woff │ │ └── fontawesome-webfontd41d.eot │ ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regulard41d.eot │ └── js │ ├── Atp.js │ ├── bootstrap.min.js │ ├── custom-scripts.js │ ├── custom.js │ ├── dataTables │ ├── dataTables.bootstrap.css │ ├── dataTables.bootstrap.js │ └── jquery.dataTables.js │ ├── jquery-1.10.2.js │ ├── jquery.metisMenu.js │ ├── morris │ ├── morris-0.4.3.min.css │ ├── morris.js │ └── raphael-2.1.0.min.js │ └── web-report-default.min.js └── templates ├── IEDriverServer.exe ├── chromedriver.exe ├── geckodriver.exe └── page ├── 1登录.html ├── 2项目管理--环境配置.html ├── 2项目管理.html ├── 3页面管理.html ├── 4页面元素.html ├── 5-1新建关键字.html ├── 5-2编辑关键字.html ├── 5关键字库.html ├── 6-1新建测试用例.html ├── 6-1编辑测试用例.html ├── 6测试用例.html ├── 7-1查看测试结果.html ├── 7测试结果.html ├── 8-1新建登录配置.html ├── 8-1编辑登录配置.html ├── 8登录配置.html ├── 9任务管理.html └── 首页.html /.gitignore: -------------------------------------------------------------------------------- 1 | ./mysql 2 | .idea 3 | .python-version 4 | __pycache__ 5 | celerybeat-schedule.bak 6 | celerybeat-schedule.dat 7 | celerybeat-schedule.dir -------------------------------------------------------------------------------- /Admin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__init__.py -------------------------------------------------------------------------------- /Admin/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Admin/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Admin/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /Admin/__pycache__/admin.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__pycache__/admin.cpython-37.pyc -------------------------------------------------------------------------------- /Admin/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /Admin/__pycache__/models.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__pycache__/models.cpython-37.pyc -------------------------------------------------------------------------------- /Admin/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /Admin/__pycache__/views.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/__pycache__/views.cpython-37.pyc -------------------------------------------------------------------------------- /Admin/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /Admin/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AdminConfig(AppConfig): 5 | name = 'Admin' 6 | -------------------------------------------------------------------------------- /Admin/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/migrations/__init__.py -------------------------------------------------------------------------------- /Admin/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Admin/migrations/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Admin/migrations/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Admin/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /Admin/tests.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | 3 | # Create your tests here. 4 | 5 | 6 | 7 | 8 | driver.get('http://www.baidu.com') 9 | print(driver.title) 10 | -------------------------------------------------------------------------------- /Admin/views.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | from django.shortcuts import render 3 | from django.http import HttpResponse, HttpResponseRedirect, JsonResponse 4 | from django.contrib.auth.decorators import login_required 5 | from django.contrib import auth 6 | from django.contrib.auth import authenticate,login 7 | from django.shortcuts import render 8 | from django.contrib import messages 9 | import json 10 | import os 11 | import pymysql 12 | import time 13 | from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 14 | 15 | 16 | # Create your views here. 17 | def login(request): 18 | if request.POST: 19 | username = password = '' 20 | username = request.POST.get('username') 21 | password = request.POST.get('password') 22 | user = auth.authenticate(username=username,password=password) #认证给出的用户名和密码 23 | if user is not None and user.is_active: #判断用户名和密码是否有效 24 | auth.login(request, user) 25 | request.session['user'] = username #跨请求的保持user参数 26 | response = HttpResponseRedirect('/admin/index') 27 | return response 28 | else: 29 | messages.add_message(request, messages.WARNING, '账户或者密码错误,请检查') 30 | return render(request, 'page/1登录.html') 31 | 32 | return render(request, 'page/1登录.html') 33 | 34 | @login_required 35 | def logout(request): 36 | auth.logout(request) 37 | return render(request, 'page/1登录.htm') 38 | 39 | 40 | @login_required 41 | def index(request): 42 | return render(request, "page/首页.html") 43 | 44 | 45 | @login_required 46 | def project(request): 47 | return render(request, "page/2项目管理.html") 48 | 49 | 50 | @login_required 51 | def project_config(request, project_id): 52 | from Product.models import Project 53 | from Autotest_platform.helper.util import get_model 54 | p = get_model(Project, id=project_id) 55 | name = p.name if p else "" 56 | return render(request, "page/2项目管理--环境配置.html", {"projectId": project_id, "projectName": name}) 57 | 58 | 59 | @login_required 60 | def page(request): 61 | return render(request, "page/3页面管理.html") 62 | 63 | 64 | @login_required 65 | def element(request): 66 | return render(request, "page/4页面元素.html") 67 | 68 | 69 | @login_required 70 | def keyword(request): 71 | return render(request, "page/5关键字库.html") 72 | 73 | 74 | @login_required 75 | def keyword_create(request): 76 | return render(request, "page/5-1新建关键字.html") 77 | 78 | 79 | @login_required 80 | def keyword_edit(request, keyword_id): 81 | from Product.models import Keyword, Project 82 | from Autotest_platform.helper.util import get_model 83 | kw = get_model(Keyword, id=keyword_id) 84 | projectId = kw.projectId if kw else 0 85 | p = get_model(Project, id=projectId) 86 | projectName = p.name if projectId > 0 and p else "通用" 87 | keywordName = kw.name if kw else "" 88 | return render(request, "page/5-2编辑关键字.html", 89 | {"id": projectId, "projectName": projectName, "keywordId": keyword_id, "keywordName": keywordName}) 90 | 91 | 92 | @login_required 93 | def testcase(request): 94 | return render(request, "page/6测试用例.html") 95 | 96 | 97 | @login_required 98 | def testcase_create(request): 99 | return render(request, "page/6-1新建测试用例.html") 100 | 101 | 102 | @login_required 103 | def testcase_edit(request, testcase_id): 104 | return render(request, "page/6-1编辑测试用例.html", {"testcaseId": testcase_id}) 105 | 106 | 107 | @login_required 108 | def result(request): 109 | return render(request, "page/7测试结果.html") 110 | 111 | 112 | @login_required 113 | def result_see(request, result_id): 114 | return render(request, "page/7-1查看测试结果.html", {"id": result_id}) 115 | 116 | 117 | @login_required 118 | def task(request): 119 | return render(request, "page/9任务管理.html") 120 | 121 | 122 | @login_required 123 | def loginConfig(request): 124 | return render(request, "page/8登录配置.html") 125 | 126 | 127 | @login_required 128 | def loginConfig_create(request): 129 | return render(request, "page/8-1新建登录配置.html") 130 | 131 | 132 | @login_required 133 | def loginConfig_edit(request, login_id): 134 | return render(request, "page/8-1编辑登录配置.html", {"id": login_id}) 135 | -------------------------------------------------------------------------------- /Autotest_platform/PageObject/Base.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.support.wait import WebDriverWait 2 | from selenium.webdriver.support import expected_conditions as ec 3 | from Product.models import Element 4 | from selenium.webdriver.common.action_chains import ActionChains 5 | from selenium.webdriver.support.select import Select 6 | import time 7 | import requests 8 | import json 9 | import random 10 | 11 | class PageObject: 12 | driver = None 13 | 14 | def sleep(self, second): 15 | if str(second).isdigit(): 16 | time.sleep(int(second)) 17 | else: 18 | time.sleep(0.5) 19 | 20 | def wait(self, seconds): 21 | self.driver.implicitly_wait(seconds) 22 | 23 | def open_url(self, url): 24 | self.driver.get(url) 25 | 26 | def max_size(self): 27 | self.driver.maximize_window() 28 | 29 | def click(self, locator): 30 | if locator is None: 31 | return 32 | else: 33 | try: 34 | PageObject.find_element(self.driver, locator).click() 35 | except: 36 | raise 37 | 38 | def click_point(self, x, y, left_click=True): 39 | if left_click: 40 | ActionChains(self.driver).move_by_offset(x, y - 103).click().perform() 41 | else: 42 | ActionChains(self.driver).move_by_offset(x, y - 103).context_click().perform() 43 | 44 | def send_keys(self, locator, value): 45 | if locator is None: 46 | return 47 | if value is None: 48 | self.clear(locator) 49 | else: 50 | PageObject.find_element(self.driver, locator).send_keys(value) 51 | 52 | def clear(self, locator): 53 | if locator is None: 54 | return 55 | 56 | def alert_accept(self): 57 | self.driver.switch_to.alert().accept() 58 | 59 | def alert_dismiss(self): 60 | self.driver.switch_to.alert().dismiss() 61 | 62 | def switch_to_window(self, title=None): 63 | handle = self.driver.current_window_handle 64 | if title: 65 | for handle_ in self.driver.window_handles: 66 | if handle != handle_: 67 | self.driver.switch_to.window(handle) 68 | if self.driver.title == title: 69 | break 70 | else: 71 | raise ValueError("未找到标题为:" + title + " 的页面") 72 | else: 73 | for handle_ in self.driver.window_handles: 74 | if handle != handle_: 75 | self.driver.switch_to.window(handle) 76 | 77 | def switch_to_frame(self, locator=None): 78 | if locator: 79 | self.driver.switch_to.frame(PageObject.find_element(self.driver, locator)) 80 | else: 81 | self.driver.switch_to.default_content() 82 | 83 | def forward(self): 84 | self.driver.forward() 85 | 86 | def back(self): 87 | self.driver.back() 88 | self.sleep(1) 89 | 90 | def refresh(self): 91 | self.driver.refresh(); 92 | 93 | def close(self): 94 | self.driver.close() 95 | 96 | def quit(self): 97 | self.driver.quit() 98 | 99 | def select_by_text(self, element, value, visible=False): 100 | if element is None: 101 | return 102 | element = PageObject.find_element(self.driver, element) 103 | if not visible: 104 | Select(element).select_by_text(value) 105 | else: 106 | Select(element).select_by_visible_text(value) 107 | 108 | @staticmethod 109 | def find_element(driver, locator, more=False, timeout=20): 110 | message = locator 111 | if isinstance(locator, dict): 112 | locator = (locator.get("by", None), locator.get("locator", None)) 113 | message = locator 114 | elif isinstance(locator, list) and len(locator) > 2: 115 | locator = (locator[0], locator[1]) 116 | message = locator 117 | elif isinstance(locator, Element): 118 | message = locator.name 119 | locator = (locator.by, locator.locator) 120 | elif isinstance(locator, str): 121 | locator = tuple(locator.split(".", 1)) 122 | message = locator 123 | else: 124 | raise TypeError("element参数类型错误: type:" + str(type(locator))) 125 | try: 126 | try: 127 | if more: 128 | return WebDriverWait(driver, timeout).until(ec.visibility_of_all_elements_located(locator)) 129 | else: 130 | return WebDriverWait(driver, timeout).until(ec.visibility_of_element_located(locator)) 131 | except: 132 | if more: 133 | return WebDriverWait(driver, timeout).until(ec.presence_of_all_elements_located(locator)) 134 | else: 135 | return WebDriverWait(driver, timeout).until(ec.presence_of_element_located(locator)) 136 | except Exception: 137 | raise RuntimeError("找不到元素:" + str(message)) 138 | 139 | def move_to_element(self, locator): 140 | ActionChains(self.driver).move_to_element(self.find_element(self.driver, locator)).perform() 141 | 142 | # def autologin(self, locator1, locator2): 143 | # telephone = '181'+''.join(str(random.choice(range(10))) for _ in range(8)) 144 | # payload = {"telephone": telephone} 145 | # r = requests.post('https://your_website/your_api/code', data = payload) 146 | # dic = r.json() 147 | # code = dic['data']['code'] 148 | # self.sleep(3) 149 | # self.send_keys(locator1, telephone) 150 | # self.send_keys(locator2, code) 151 | 152 | def move_jindutiao(self, locator): 153 | self.driver.execute_script("arguments[0].scrollIntoView();", PageObject.find_element(self.driver, locator)) 154 | 155 | # 鼠标悬停 156 | def xuanting(self, locator): 157 | el = PageObject.find_element(self.driver, locator) 158 | ActionChains(self.driver).move_to_element(el).perform() 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /Autotest_platform/PageObject/Base_m.py: -------------------------------------------------------------------------------- 1 | import time 2 | from appium import webdriver 3 | from selenium.common.exceptions import NoSuchElementException 4 | from selenium.webdriver.common.action_chains import ActionChains 5 | from selenium.webdriver.common.keys import Keys 6 | from selenium.webdriver.common.touch_actions import TouchActions 7 | from selenium.webdriver.common.by import By 8 | from selenium.webdriver.support.ui import WebDriverWait 9 | from selenium.webdriver.support import expected_conditions as EC 10 | 11 | class PageObject: 12 | driver = None 13 | 14 | def sleep(self, second): 15 | if str(second).isdigit(): 16 | time.sleep(int(second)) 17 | else: 18 | time.sleep(0.5) 19 | 20 | def wait(self, seconds): 21 | self.driver.implicitly_wait(seconds) 22 | 23 | # def open_baidu(self): 24 | # self.driver.find_element_by_id("com.android.browser:id/search_hint").click() 25 | # self.sleep(2) 26 | # self.driver.find_element_by_id("com.android.browser:id/url").send_keys("m.baidu.com") 27 | # self.sleep(3) 28 | # self.driver.find_element_by_id("com.android.browser:id/rightText").click() 29 | # self.sleep(6) 30 | # self.wait(4) 31 | 32 | @staticmethod 33 | def find_element(driver, locator, more=False, timeout=20): 34 | message = locator 35 | if isinstance(locator, dict): 36 | locator = (locator.get("by", None), locator.get("locator", None)) 37 | message = locator 38 | elif isinstance(locator, list) and len(locator) > 2: 39 | locator = (locator[0], locator[1]) 40 | message = locator 41 | elif isinstance(locator, Element): 42 | message = locator.name 43 | locator = (locator.by, locator.locator) 44 | elif isinstance(locator, str): 45 | locator = tuple(locator.split(".", 1)) 46 | message = locator 47 | else: 48 | raise TypeError("element参数类型错误: type:" + str(type(locator))) 49 | try: 50 | try: 51 | if more: 52 | return WebDriverWait(driver, timeout).until(ec.visibility_of_all_elements_located(locator)) 53 | else: 54 | return WebDriverWait(driver, timeout).until(ec.visibility_of_element_located(locator)) 55 | except: 56 | if more: 57 | return WebDriverWait(driver, timeout).until(ec.presence_of_all_elements_located(locator)) 58 | else: 59 | return WebDriverWait(driver, timeout).until(ec.presence_of_element_located(locator)) 60 | except Exception: 61 | raise RuntimeError("找不到元素:" + str(message)) -------------------------------------------------------------------------------- /Autotest_platform/PageObject/Gift.py: -------------------------------------------------------------------------------- 1 | from .Base import PageObject 2 | 3 | 4 | class index(PageObject): 5 | pass 6 | 7 | 8 | class create(PageObject): 9 | pass 10 | 11 | 12 | class update(PageObject): 13 | pass 14 | -------------------------------------------------------------------------------- /Autotest_platform/PageObject/__init__.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver -------------------------------------------------------------------------------- /Autotest_platform/PageObject/__pycache__/Base.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/PageObject/__pycache__/Base.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/PageObject/__pycache__/Base.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/PageObject/__pycache__/Base.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/PageObject/__pycache__/Base_m.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/PageObject/__pycache__/Base_m.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/PageObject/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/PageObject/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/PageObject/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/PageObject/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/PageObject/logger.py: -------------------------------------------------------------------------------- 1 | # _*_ coding: utf-8 _*_ 2 | import logging 3 | import os.path 4 | import time 5 | import sys 6 | sys.path.append(os.path.dirname(os.path.abspath('.'))) 7 | 8 | class Logger(object): 9 | 10 | def __init__(self, logger): 11 | ''' 12 | 指定保存日志的文件路径,日志级别,以及调用文件 13 | 将日志存入到指定的文件中 14 | ''' 15 | 16 | # 创建一个logger 17 | self.logger = logging.getLogger(logger) 18 | self.logger.setLevel(logging.DEBUG) 19 | 20 | # 创建一个handler,用于写入日志文件 21 | rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time())) 22 | # log_path = os.path.dirname(os.getcwd()) + '/Logs/' # 项目根目录下/Logs 保存日志 23 | log_path = os.path.dirname(os.path.abspath('.')) + '/logs/' 24 | # 如果case组织结构式 /testsuit/featuremodel/xxx.py , 那么得到的相对路径的父路径就是项目根目录 25 | log_name = log_path + rq + '.log' 26 | fh = logging.FileHandler(log_name) 27 | fh.setLevel(logging.INFO) 28 | 29 | # 再创建一个handler,用于输出到控制台 30 | ch = logging.StreamHandler() 31 | ch.setLevel(logging.INFO) 32 | 33 | # 定义handler的输出格式 34 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 35 | fh.setFormatter(formatter) 36 | ch.setFormatter(formatter) 37 | 38 | # 给logger添加handler 39 | self.logger.addHandler(fh) 40 | self.logger.addHandler(ch) 41 | 42 | def getlog(self): 43 | return self.logger -------------------------------------------------------------------------------- /Autotest_platform/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | from .celery import app as celery_app 3 | import pymysql 4 | 5 | pymysql.install_as_MySQLdb() 6 | __all__ = ['celery_app'] 7 | -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/celery.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/celery.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/celery.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/celery.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/settings.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/settings.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/settings.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/settings.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/urls.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/urls.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/wsgi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/wsgi.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/__pycache__/wsgi.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/__pycache__/wsgi.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/celery.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | 3 | import os 4 | 5 | from celery import Celery 6 | 7 | # 设置环境变量 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Autotest_platform.settings') 9 | 10 | # 注册Celery的APP 11 | app = Celery('Autotest_platform') 12 | # 绑定配置文件 13 | app.config_from_object('django.conf:settings') 14 | 15 | # 自动发现各个app下的tasks.py文件 16 | app.autodiscover_tasks(['Product'], force=True) 17 | -------------------------------------------------------------------------------- /Autotest_platform/helper/Http.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import json 3 | 4 | 5 | class JsonResponse(HttpResponse): 6 | def __init__(self, code=200, message="ok", data=None): 7 | from django.core.serializers.json import json 8 | response = dict() 9 | response['code'] = code 10 | response['message'] = message 11 | response['data'] = data 12 | super(JsonResponse, self).__init__(json.dumps(response, ensure_ascii=False), content_type="application/json",) 13 | self["Access-Control-Allow-Origin"] = "*" 14 | self["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS" 15 | self["Access-Control-Max-Age"] = "1000" 16 | self["Access-Control-Allow-Headers"] = "*" 17 | self["Accept"] = "*" 18 | 19 | 20 | @staticmethod 21 | def OK(message="ok", data=None): 22 | response = JsonResponse(200, message, data) 23 | return response 24 | 25 | @staticmethod 26 | def BadRequest(message="Bad request", data=None): 27 | response = JsonResponse(400, message, data) 28 | return response 29 | 30 | @staticmethod 31 | def Unauthorized(message="Unauthorized", data=None): 32 | return JsonResponse(401, message, data) 33 | 34 | @staticmethod 35 | def MethodNotAllowed(message="Method not allowed", data=None): 36 | return JsonResponse(405, message, data) 37 | 38 | @staticmethod 39 | def ServerError(message="Internal server error", data=None): 40 | return JsonResponse(500, message, data) 41 | 42 | 43 | class Session: 44 | USER = "user" 45 | 46 | 47 | # 检查请求是否为Post请求 48 | def post(fn): 49 | def request(*args, **kwargs): 50 | if args[0].method == 'POST': 51 | return fn(*args, **kwargs) 52 | else: 53 | return JsonResponse.MethodNotAllowed("请使用Post请求") 54 | 55 | return request 56 | 57 | 58 | # 检查请求是否携带登陆信息 59 | def check_login(fn): 60 | def _check_login(*args, **kwargs): 61 | try: 62 | if not (args[0].session.get(Session.USER, None)): 63 | return JsonResponse.Unauthorized("未检测到登陆信息") 64 | return fn(*args, **kwargs) 65 | except: 66 | return JsonResponse.ServerError("检查登陆状态时出错") 67 | 68 | return _check_login 69 | 70 | 71 | def get_request_body(request): 72 | try: 73 | content = request.body.decode() 74 | content = json.loads(request.body.decode("utf-8")) if content else {} 75 | except: 76 | raise ValueError 77 | return content 78 | -------------------------------------------------------------------------------- /Autotest_platform/helper/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Autotest_platform/helper/__pycache__/Http.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/helper/__pycache__/Http.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/helper/__pycache__/Http.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/helper/__pycache__/Http.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/helper/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/helper/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/helper/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/helper/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/helper/__pycache__/util.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/helper/__pycache__/util.cpython-36.pyc -------------------------------------------------------------------------------- /Autotest_platform/helper/__pycache__/util.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Autotest_platform/helper/__pycache__/util.cpython-37.pyc -------------------------------------------------------------------------------- /Autotest_platform/helper/util.py: -------------------------------------------------------------------------------- 1 | def get_model(model, get=True, *args, **kwargs, ): 2 | from django.db.models.base import ModelBase 3 | if isinstance(model, ModelBase): 4 | if get: 5 | try: 6 | return model.objects.get(*args, **kwargs) 7 | except: 8 | return None 9 | else: 10 | return model.objects.filter(*args, **kwargs) 11 | else: 12 | raise TypeError("model 没有继承 django.db.models.base.ModelBase") 13 | 14 | 15 | def isLegal(string, length=5, match_='([^a-z0-9A-Z_])+'): 16 | import re 17 | pattern = re.compile(match_) 18 | match = pattern.findall(string) 19 | if string and len(string) > length: 20 | if match: 21 | return False 22 | else: 23 | return True 24 | else: 25 | return False 26 | 27 | def md5(string): 28 | import hashlib 29 | return hashlib.md5(string.encode()).hexdigest() 30 | 31 | def validateEmail(email): 32 | import re 33 | if len(email) > 7: 34 | if re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", email): 35 | return True 36 | return False -------------------------------------------------------------------------------- /Autotest_platform/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for Autotest_platform project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | import djcelery 15 | from celery.schedules import crontab 16 | from datetime import timedelta 17 | 18 | djcelery.setup_loader() 19 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 20 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 21 | 22 | # Quick-start development settings - unsuitable for production 23 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ 24 | 25 | # SECURITY WARNING: keep the secret key used in production secret! 26 | SECRET_KEY = '#zpo_me0s492$q_8-2qav%53jx+965%3qx9j0eyqzf8yx=%%rr' 27 | 28 | # SECURITY WARNING: don't run with debug turned on in production! 29 | DEBUG = True 30 | 31 | ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '0.0.0.0'] 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'djcelery', 41 | 'Product', 42 | 'Admin', 43 | ] 44 | 45 | # AUTH_USER_MODEL = 'Product.User' 46 | 47 | MIDDLEWARE = [ 48 | 'django.middleware.gzip.GZipMiddleware', 49 | 'django.middleware.security.SecurityMiddleware', 50 | 'django.contrib.sessions.middleware.SessionMiddleware', 51 | # 'django.middleware.csrf.CsrfViewMiddleware', 52 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 53 | 'django.contrib.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | ] 56 | 57 | ROOT_URLCONF = 'Autotest_platform.urls' 58 | 59 | TEMPLATES = [ 60 | { 61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 62 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 63 | 'APP_DIRS': True, 64 | 'OPTIONS': { 65 | 'context_processors': [ 66 | 'django.template.context_processors.debug', 67 | 'django.template.context_processors.request', 68 | 'django.contrib.auth.context_processors.auth', 69 | 'django.contrib.messages.context_processors.messages', 70 | ], 71 | }, 72 | }, 73 | ] 74 | 75 | WSGI_APPLICATION = 'Autotest_platform.wsgi.application' 76 | 77 | # Database 78 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 79 | 80 | DATABASES = { 81 | 'default': { 82 | 'ENGINE': 'django.db.backends.mysql', 83 | 'NAME': 'autotest', 84 | 'USER': 'root', 85 | 'PASSWORD': '123456', 86 | } 87 | } 88 | 89 | # Password validation 90 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 91 | 92 | AUTH_PASSWORD_VALIDATORS = [ 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 104 | }, 105 | ] 106 | 107 | # Internationalization 108 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 109 | 110 | LANGUAGE_CODE = 'en-us' 111 | 112 | TIME_ZONE = 'Asia/Shanghai' 113 | 114 | USE_I18N = True 115 | 116 | USE_L10N = True 117 | 118 | USE_TZ = False 119 | 120 | CELERY_BROKER_URL = 'amqp://guest:guest@localhost:5672' 121 | CELERY_ACCEPT_CONTENT = ['pickle', 'json', 'msgpack', 'yaml'] 122 | # BROKER_URL = 'amqp://guest:guest@localhost:5672' 123 | # CELERY_RESULT_BACKEND = 'amqp://guest:guest@localhost:5672' 124 | 125 | STATIC_URL = '/static/' 126 | 127 | STATICFILES_DIRS = ( 128 | os.path.join(BASE_DIR, "static"), 129 | ) 130 | 131 | CELERYBEAT_SCHEDULE = { 132 | 'timing': { 133 | 'task': 'Product.tasks.timingRunning', 134 | # 'schedule': crontab(hour=10, minute=30), 135 | 'schedule': timedelta(seconds=300), 136 | }, 137 | } 138 | 139 | # AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend'] 140 | # STATIC_ROOT = os.path.join(BASE_DIR, 'static') -------------------------------------------------------------------------------- /Autotest_platform/urls.py: -------------------------------------------------------------------------------- 1 | """Autotest_platform URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.conf.urls import url 18 | from django.urls import include, path, re_path 19 | from django.conf.urls.static import static 20 | from django.views.static import serve 21 | from django.conf import settings 22 | from Admin.views import * 23 | from Product.views import Project, Page, Element, Keyword, TestCase, TestResult, Public, TestTasks, Environment, Login#, \ 24 | #User 25 | 26 | urlpatterns = [ 27 | # project 28 | path('api/v1/project/create', Project.create), 29 | path('api/v1/project/delete/', Project.delete), 30 | path('api/v1/project/edit/', Project.edit), 31 | path('api/v1/project', Project.find), 32 | path('api/v1/project/', Project.get), 33 | # environment 34 | path('api/v1/environment/create', Environment.create), 35 | path('api/v1/environment/delete/', Environment.delete), 36 | path('api/v1/environment/edit/', Environment.edit), 37 | path('api/v1/environment', Environment.find), 38 | path('api/v1/environment/', Environment.get), 39 | # page 40 | path('api/v1/page/create', Page.create), 41 | path('api/v1/page/delete/', Page.delete), 42 | path('api/v1/page/edit/', Page.edit), 43 | path('api/v1/page', Page.find), 44 | path('api/v1/page/', Page.get), 45 | # element 46 | path('api/v1/element/create', Element.create), 47 | path('api/v1/element/delete/', Element.delete), 48 | path('api/v1/element/edit/', Element.edit), 49 | path('api/v1/element', Element.find), 50 | path('api/v1/element/', Element.get), 51 | # keyword 52 | path('api/v1/keyword/create', Keyword.create), 53 | path('api/v1/keyword/delete/', Keyword.delete), 54 | path('api/v1/keyword/edit/', Keyword.edit), 55 | path('api/v1/keyword', Keyword.find), 56 | path('api/v1/keyword/', Keyword.get), 57 | # testcase 58 | path('api/v1/testcase/create', TestCase.create), 59 | path('api/v1/testcase/delete/', TestCase.delete), 60 | path('api/v1/testcase/edit/', TestCase.edit), 61 | path('api/v1/testcase', TestCase.find), 62 | path('api/v1/testcase/', TestCase.get), 63 | path('api/v1/testcase/copy/', TestCase.copy), 64 | # tasks 65 | path('api/v1/task/create', TestTasks.create), 66 | path('api/v1/task/delete/', TestTasks.delete), 67 | path('api/v1/task/edit/', TestTasks.edit), 68 | path('api/v1/task', TestTasks.find), 69 | path('api/v1/task/', TestTasks.get), 70 | path('api/v1/task/running/', TestTasks.test), 71 | # Login 72 | path('api/v1/login/create', Login.create), 73 | path('api/v1/login/delete/', Login.delete), 74 | path('api/v1/login/edit/', Login.edit), 75 | path('api/v1/login', Login.find), 76 | path('api/v1/login/', Login.get), 77 | path('api/v1/login/bind/', Login.bind), 78 | path('api/v1/login/unbind/', Login.unbind), 79 | path('api/v1/login/bind/edit/', Login.edit_bind), 80 | 81 | 82 | path('api/v1/testcase/running/', TestCase.test), 83 | path('api/v1/result', TestResult.find), 84 | path('api/v1/result/delete/', TestResult.delete), 85 | path('api/v1/result/', TestResult.get), 86 | path('api/v1/browser', Public.data), 87 | path('api/v1/projectSummary', Public.index), 88 | path('api/v1/barChar', Public.bar_char), 89 | path('api/v1/lineChar', Public.line_char), 90 | 91 | 92 | path('admin/', admin.site.urls), 93 | path('login/', login), 94 | path('logout/', logout), 95 | path('admin/index', index), 96 | path('admin/project', project), 97 | path('admin/project/', project_config), 98 | path('admin/page', page), 99 | path('admin/element', element), 100 | path('admin/keyword', keyword), 101 | path('admin/keyword/create', keyword_create), 102 | path('admin/keyword/edit/', keyword_edit), 103 | path('admin/testcase', testcase), 104 | path('admin/testcase/create', testcase_create), 105 | path('admin/testcase/', testcase_edit), 106 | path('admin/loginConfig', loginConfig), 107 | path('admin/loginConfig/create', loginConfig_create), 108 | path('admin/loginConfig/edit/', loginConfig_edit), 109 | path('admin/task', task), 110 | path('admin/result', result), 111 | path('admin/result/', result_see), 112 | 113 | ] 114 | -------------------------------------------------------------------------------- /Autotest_platform/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for Autotest_platform project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Autotest_platform.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /Product/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__init__.py -------------------------------------------------------------------------------- /Product/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Product/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Product/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /Product/__pycache__/admin.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/admin.cpython-37.pyc -------------------------------------------------------------------------------- /Product/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /Product/__pycache__/models.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/models.cpython-37.pyc -------------------------------------------------------------------------------- /Product/__pycache__/tasks.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/tasks.cpython-36.pyc -------------------------------------------------------------------------------- /Product/__pycache__/tasks.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/tasks.cpython-37.pyc -------------------------------------------------------------------------------- /Product/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /Product/__pycache__/views.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/__pycache__/views.cpython-37.pyc -------------------------------------------------------------------------------- /Product/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | # from .models import User 3 | # # Register your models here. 4 | 5 | # admin.site.register(User) -------------------------------------------------------------------------------- /Product/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProductConfig(AppConfig): 5 | name = 'Product' 6 | -------------------------------------------------------------------------------- /Product/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.5 on 2018-07-30 18:49 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Browser', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('name', models.CharField(max_length=20)), 20 | ('value', models.CharField(max_length=20)), 21 | ('remark', models.TextField(null=True)), 22 | ('installPath', models.TextField(null=True)), 23 | ('driverPath', models.TextField(null=True)), 24 | ], 25 | options={ 26 | 'db_table': 'Browser', 27 | }, 28 | ), 29 | migrations.CreateModel( 30 | name='Element', 31 | fields=[ 32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 33 | ('projectId', models.IntegerField()), 34 | ('pageId', models.IntegerField()), 35 | ('name', models.CharField(max_length=20)), 36 | ('remark', models.TextField(null=True)), 37 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 38 | ('by', models.CharField(max_length=20)), 39 | ('locator', models.CharField(max_length=200)), 40 | ], 41 | options={ 42 | 'db_table': 'element', 43 | }, 44 | ), 45 | migrations.CreateModel( 46 | name='Environment', 47 | fields=[ 48 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 49 | ('projectId', models.IntegerField(null=True)), 50 | ('name', models.CharField(max_length=20)), 51 | ('host', models.TextField()), 52 | ('remark', models.TextField(null=True)), 53 | ], 54 | options={ 55 | 'db_table': 'Environment', 56 | }, 57 | ), 58 | migrations.CreateModel( 59 | name='EnvironmentLogin', 60 | fields=[ 61 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 62 | ('loginId', models.IntegerField()), 63 | ('environmentId', models.IntegerField()), 64 | ('parameter', models.TextField()), 65 | ], 66 | options={ 67 | 'db_table': 'EnvironmentLogin', 68 | }, 69 | ), 70 | migrations.CreateModel( 71 | name='Keyword', 72 | fields=[ 73 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 74 | ('projectId', models.IntegerField()), 75 | ('name', models.CharField(max_length=20)), 76 | ('type', models.IntegerField(default=2)), 77 | ('package', models.CharField(max_length=200, null=True)), 78 | ('clazz', models.CharField(max_length=50, null=True)), 79 | ('method', models.CharField(max_length=50, null=True)), 80 | ('params', models.TextField(null=True)), 81 | ('steps', models.TextField(null=True)), 82 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 83 | ('remark', models.TextField(null=True)), 84 | ], 85 | options={ 86 | 'db_table': 'keyword', 87 | }, 88 | ), 89 | migrations.CreateModel( 90 | name='LoginConfig', 91 | fields=[ 92 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 93 | ('projectId', models.IntegerField()), 94 | ('name', models.CharField(max_length=20)), 95 | ('remark', models.TextField(null=True)), 96 | ('checkType', models.TextField(default='')), 97 | ('checkValue', models.TextField(default='')), 98 | ('steps', models.TextField()), 99 | ('params', models.TextField()), 100 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 101 | ], 102 | options={ 103 | 'db_table': 'login', 104 | }, 105 | ), 106 | migrations.CreateModel( 107 | name='Page', 108 | fields=[ 109 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 110 | ('projectId', models.IntegerField()), 111 | ('name', models.CharField(max_length=20)), 112 | ('remark', models.TextField(null=True)), 113 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 114 | ], 115 | options={ 116 | 'db_table': 'page', 117 | }, 118 | ), 119 | migrations.CreateModel( 120 | name='Project', 121 | fields=[ 122 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 123 | ('name', models.CharField(max_length=20)), 124 | ('remark', models.TextField(null=True)), 125 | ('creator', models.IntegerField()), 126 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 127 | ], 128 | options={ 129 | 'db_table': 'project', 130 | }, 131 | ), 132 | migrations.CreateModel( 133 | name='Result', 134 | fields=[ 135 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 136 | ('title', models.CharField(max_length=200)), 137 | ('taskId', models.IntegerField(default=0, null=True)), 138 | ('projectId', models.IntegerField()), 139 | ('testcaseId', models.IntegerField()), 140 | ('browsers', models.TextField(null=True)), 141 | ('beforeLogin', models.TextField(null=True)), 142 | ('environments', models.TextField(null=True)), 143 | ('status', models.IntegerField(default=10)), 144 | ('parameter', models.TextField()), 145 | ('steps', models.TextField()), 146 | ('checkType', models.TextField()), 147 | ('checkValue', models.TextField()), 148 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 149 | ], 150 | options={ 151 | 'db_table': 'Result', 152 | }, 153 | ), 154 | migrations.CreateModel( 155 | name='SplitResult', 156 | fields=[ 157 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 158 | ('environmentId', models.IntegerField(null=True)), 159 | ('browserId', models.IntegerField(null=True)), 160 | ('resultId', models.IntegerField()), 161 | ('loginStatus', models.IntegerField(default=0)), 162 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 163 | ('startTime', models.DateTimeField(null=True)), 164 | ('finishTime', models.DateTimeField(null=True)), 165 | ('parameter', models.TextField()), 166 | ('expect', models.BooleanField()), 167 | ('status', models.IntegerField(default=10)), 168 | ('remark', models.TextField(null=True)), 169 | ], 170 | options={ 171 | 'db_table': 'SplitResult', 172 | }, 173 | ), 174 | migrations.CreateModel( 175 | name='Task', 176 | fields=[ 177 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 178 | ('name', models.CharField(max_length=200)), 179 | ('testcases', models.TextField()), 180 | ('browsers', models.TextField(null=True)), 181 | ('status', models.IntegerField(default=1, null=True)), 182 | ('timing', models.IntegerField(default=1)), 183 | ('remark', models.TextField(null=True)), 184 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 185 | ], 186 | options={ 187 | 'db_table': 'Task', 188 | }, 189 | ), 190 | migrations.CreateModel( 191 | name='TestCase', 192 | fields=[ 193 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 194 | ('projectId', models.IntegerField()), 195 | ('title', models.CharField(max_length=200)), 196 | ('level', models.IntegerField(default=1)), 197 | ('beforeLogin', models.TextField(null=True)), 198 | ('steps', models.TextField()), 199 | ('parameter', models.TextField()), 200 | ('checkType', models.TextField()), 201 | ('checkValue', models.TextField()), 202 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 203 | ('remark', models.TextField(null=True)), 204 | ], 205 | options={ 206 | 'db_table': 'testcase', 207 | }, 208 | ), 209 | migrations.CreateModel( 210 | name='User', 211 | fields=[ 212 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 213 | ('userName', models.CharField(max_length=20)), 214 | ('password', models.CharField(max_length=50)), 215 | ('nickname', models.CharField(max_length=10, null=True)), 216 | ('group', models.IntegerField(default=1, null=True)), 217 | ('email', models.TextField(null=True)), 218 | ('createTime', models.DateTimeField(default=django.utils.timezone.now)), 219 | ], 220 | options={ 221 | 'db_table': 'user', 222 | }, 223 | ), 224 | ] 225 | -------------------------------------------------------------------------------- /Product/migrations/0002_delete_user.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-06-27 10:37 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.DeleteModel( 14 | name='User', 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /Product/migrations/0003_remove_project_creator.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-06-27 14:58 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0002_delete_user'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='project', 15 | name='creator', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /Product/migrations/0004_project_creator.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-06-27 15:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0003_remove_project_creator'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='project', 15 | name='creator', 16 | field=models.IntegerField(default=1), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /Product/migrations/0005_auto_20190627_1542.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-06-27 15:42 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0004_project_creator'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='project', 15 | name='creator', 16 | field=models.CharField(default='少年', max_length=20), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /Product/migrations/0006_loginconfig_logintype.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-07-02 11:32 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0005_auto_20190627_1542'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='loginconfig', 15 | name='loginType', 16 | field=models.IntegerField(default=1), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /Product/migrations/0007_environmentlogin_logintype.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-07-02 14:42 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0006_loginconfig_logintype'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='environmentlogin', 15 | name='loginType', 16 | field=models.IntegerField(default=1), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /Product/migrations/0008_auto_20190702_1507.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-07-02 15:07 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0007_environmentlogin_logintype'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='environmentlogin', 15 | name='loginType', 16 | ), 17 | migrations.RemoveField( 18 | model_name='loginconfig', 19 | name='loginType', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /Product/migrations/0009_auto_20190719_1135.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-07-19 11:35 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0008_auto_20190702_1507'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='loginconfig', 15 | name='checkText', 16 | field=models.TextField(default=''), 17 | ), 18 | migrations.AddField( 19 | model_name='result', 20 | name='checkText', 21 | field=models.TextField(null=True), 22 | ), 23 | migrations.AddField( 24 | model_name='testcase', 25 | name='checkText', 26 | field=models.TextField(null=True), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /Product/migrations/0010_auto_20190722_1136.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.7 on 2019-07-22 11:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('Product', '0009_auto_20190719_1135'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='loginconfig', 15 | name='selectText', 16 | field=models.TextField(default=''), 17 | ), 18 | migrations.AddField( 19 | model_name='result', 20 | name='selectText', 21 | field=models.TextField(null=True), 22 | ), 23 | migrations.AddField( 24 | model_name='testcase', 25 | name='selectText', 26 | field=models.TextField(null=True), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /Product/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__init__.py -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0001_initial.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0001_initial.cpython-37.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0002_delete_user.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0002_delete_user.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0003_remove_project_creator.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0003_remove_project_creator.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0003_user.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0003_user.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0004_project_creator.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0004_project_creator.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0005_auto_20190627_1542.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0005_auto_20190627_1542.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0006_loginconfig_logintype.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0006_loginconfig_logintype.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0007_environmentlogin_logintype.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0007_environmentlogin_logintype.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0008_auto_20190702_1507.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0008_auto_20190702_1507.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0009_auto_20190719_1135.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0009_auto_20190719_1135.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/0010_auto_20190722_1136.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/0010_auto_20190722_1136.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Product/migrations/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/Product/migrations/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Product/models.py: -------------------------------------------------------------------------------- 1 | import django.utils.timezone as timezone 2 | from django.core.exceptions import ValidationError 3 | from django.db import models 4 | from django.contrib.auth.models import AbstractUser 5 | import requests 6 | import json 7 | import random 8 | import time 9 | 10 | # Create your models here. 11 | class Project(models.Model): 12 | name = models.CharField(max_length=20, null=False) 13 | remark = models.TextField(null=True) 14 | creator = models.CharField(max_length=20, null=False, default='少年') 15 | createTime = models.DateTimeField(default=timezone.now) 16 | 17 | class Meta: 18 | db_table = 'project' 19 | 20 | def clean(self): 21 | name = self.name.strip() if self.name else "" 22 | if 0 >= len(name) or len(name) > 20: 23 | raise ValidationError({'name': '无效的项目名称'}) 24 | 25 | 26 | class Page(models.Model): 27 | projectId = models.IntegerField() 28 | name = models.CharField(max_length=20, null=False) 29 | remark = models.TextField(null=True) 30 | createTime = models.DateTimeField(default=timezone.now) 31 | 32 | class Meta: 33 | db_table = 'page' 34 | 35 | def clean(self): 36 | name = self.name.strip() if self.name else "" 37 | projectId = int(self.projectId) if self.projectId and str(self.projectId).isdigit() else 0 38 | if 0 >= len(name) or len(name) > 20: 39 | raise ValidationError({'name': '无效的页面名称'}) 40 | if projectId < 1: 41 | raise ValidationError({'projectId': '无效的项目Id'}) 42 | 43 | 44 | class Element(models.Model): 45 | projectId = models.IntegerField() 46 | pageId = models.IntegerField() 47 | name = models.CharField(max_length=20, null=False) 48 | remark = models.TextField(null=True) 49 | createTime = models.DateTimeField(default=timezone.now) 50 | BY_TYPES = ["id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector"] 51 | by = models.CharField(null=False, max_length=20) 52 | locator = models.CharField(max_length=200, null=False) 53 | 54 | class Meta: 55 | db_table = 'element' 56 | 57 | def __str__(self): 58 | return self.name 59 | 60 | def clean(self): 61 | name = self.name.strip() if self.name else "" 62 | locator = str(self.locator) if self.locator else "" 63 | by = str(self.by).lower() if self.by else "" 64 | # projectId = int(self.projectId) if str(self.projectId).isdigit() else 0 65 | pageId = int(self.pageId) if str(self.pageId).isdigit() else 0 66 | if 0 >= len(name) or len(name) > 20: 67 | raise ValidationError({'name': '无效的元素名称'}) 68 | # if projectId < 1: 69 | # raise ValidationError({'projectId': 'projectId'}) 70 | if pageId < 1: 71 | raise ValidationError({'pageId': '无效的页面Id'}) 72 | if not by in Element.BY_TYPES: 73 | raise ValidationError({'by': 'by'}) 74 | if 0 >= len(locator) or len(locator) > 200: 75 | raise ValidationError({'locator': '无效的定位值'}) 76 | 77 | 78 | class Keyword(models.Model): 79 | __KEYWORD_TYPES = {1: "system", 2: "custom"} 80 | projectId = models.IntegerField() 81 | name = models.CharField(max_length=20) 82 | type = models.IntegerField(default=2) 83 | package = models.CharField(max_length=200, null=True) 84 | clazz = models.CharField(max_length=50, null=True) 85 | method = models.CharField(max_length=50, null=True) 86 | params = models.TextField(null=True) 87 | steps = models.TextField(null=True) 88 | createTime = models.DateTimeField(default=timezone.now) 89 | remark = models.TextField(null=True) 90 | 91 | class Meta: 92 | db_table = "keyword" 93 | 94 | def clean(self): 95 | name = self.name.strip() if self.name else "" 96 | projectId = int(self.projectId) if str(self.projectId).isdigit() else 0 97 | package = self.package 98 | clazz = self.clazz 99 | method = self.method 100 | if not str(self.type).isdigit(): 101 | raise ValidationError({'type': '无效的操作类型'}) 102 | t = int(self.type) 103 | step = self.steps if self.steps else [] 104 | if 0 >= len(name) or len(name) > 20: 105 | raise ValidationError({'name': '无效的关键字名称'}) 106 | if projectId < 0: 107 | raise ValidationError({'projectId': '无效的项目Id'}) 108 | if t == 1: 109 | try: 110 | obj = __import__(package, fromlist=[package.split(",")[-1]]) 111 | except: 112 | raise ValidationError({'package': '无效的引用包'}) 113 | try: 114 | obj = getattr(obj, clazz) 115 | except: 116 | raise ValidationError({'clazz': '无效的引用类'}) 117 | try: 118 | getattr(obj, method) 119 | except: 120 | raise ValidationError({'method': '无效的引用方法'}) 121 | elif t == 2: 122 | if isinstance(step, str): 123 | import json 124 | step = json.loads(step) 125 | if not isinstance(step, list): 126 | raise ValidationError({'step': '无效的操作步骤 : not list'}) 127 | for s in step: 128 | if not isinstance(s, dict): 129 | raise ValidationError({'step': '无效的操作步骤'}) 130 | if not "keywordId" in s: 131 | raise ValidationError({'step': '无效的操作步骤 : keywordId'}) 132 | keywordId = int(s.get("keywordId")) if str(s.get("keywordId")).isdigit() else 0 133 | if keywordId < 1: 134 | raise ValidationError({'step': '无效的操作步骤 : keywordId'}) 135 | if not ("values" in s and isinstance(s.get("values"), list)): 136 | raise ValidationError({'step': '无效的操作步骤 : values'}) 137 | values = s.get("values") 138 | for value in values: 139 | try: 140 | Params(value) 141 | except ValueError: 142 | raise ValidationError({'step': '无效的操作步骤 : value'}) 143 | else: 144 | raise ValidationError({'type': '无效的操作类型'}) 145 | 146 | 147 | class TestCase(models.Model): 148 | # TESTCASE_TYPES = {1: "功能测试", 2: "接口测试"} 149 | TESTCASE_STATUS = {1: "未执行", 2: "排队中", 3: "执行中"} 150 | TESTCASE_LEVEL = {1: "低", 2: "中", 3: "高", } 151 | TESTCASE_CHECK_TYPE = {1: "url", 2: "element"} 152 | projectId = models.IntegerField() 153 | title = models.CharField(max_length=200, null=False) 154 | # type = models.IntegerField(null=False, default=1) 155 | level = models.IntegerField(default=1) 156 | # status = models.IntegerField(null=False) 157 | beforeLogin = models.TextField(null=True) 158 | steps = models.TextField(null=False) 159 | parameter = models.TextField() 160 | checkType = models.TextField() 161 | checkValue = models.TextField() 162 | checkText = models.TextField(null=True) 163 | selectText = models.TextField(null=True) 164 | createTime = models.DateTimeField(default=timezone.now) 165 | remark = models.TextField(null=True) 166 | 167 | class Meta: 168 | db_table = "testcase" 169 | 170 | def clean(self): 171 | projectId = self.projectId if self.projectId else 0 172 | projectId = int(self.projectId) if str(projectId).isdigit() else 0 173 | title = self.title.strip() if self.title else "" 174 | # Type = self.type 175 | level = self.level 176 | # status = self.status 177 | step = self.steps 178 | parameter = self.parameter 179 | checkType = self.checkType 180 | checkValue = self.checkValue 181 | checkText = self.checkText 182 | selectText = self.selectText 183 | login = self.beforeLogin 184 | if not isinstance(login, list): 185 | raise ValidationError({'beforeLogin': '无效的登录配置'}) 186 | if not projectId or projectId < 1: 187 | raise ValidationError({'projectId': '无效的项目Id'}) 188 | if not title or 0 >= len(title) or len(title) > 200: 189 | raise ValidationError({'title': '无效的测试用例标题'}) 190 | if not (level and level in TestCase.TESTCASE_LEVEL): 191 | raise ValidationError({'level': '无效的用例优先级'}) 192 | # if not (status and status in TestCase.TESTCASE_STATUS): 193 | # raise ValidationError({'level': 'Invalid level'}) 194 | 195 | if not isinstance(step, list): 196 | raise ValidationError({'step': '无效的操作步骤 : steps'}) 197 | for s in step: 198 | if not isinstance(s, dict): 199 | raise ValidationError({'step': '无效的操作步骤 : step'}) 200 | # if not "keywordId" in s: 201 | # raise ValidationError({'step': '无效的操作步骤 1 : keywordId'}) 202 | # keywordId = int(s.get("keywordId")) if str(s.get("keywordId")).isdigit() else 0 203 | # if keywordId < 1: 204 | # raise ValidationError({'step': '无效的操作步骤 : keywordId'}) 205 | # if not ("values" in s and isinstance(s.get("values"), list)): 206 | # raise ValidationError({'step': '无效的操作步骤 : values'}) 207 | # values = s.get("values") 208 | # for value in values: 209 | # try: 210 | # Params(value) 211 | # except ValueError: 212 | # raise ValidationError({'step': '无效的操作步骤 : value '}) 213 | if checkType: 214 | if checkValue: 215 | try: 216 | Check(checkType, checkValue) 217 | except: 218 | raise ValidationError({'check': '无效的断言'}) 219 | else: 220 | raise ValidationError({'check': '无效的断言值'}) 221 | if parameter: 222 | if isinstance(parameter, list): 223 | for p in parameter: 224 | if 'expect' not in p: 225 | raise ValidationError({'parameter': '测试数据中未找到预期结果'}) 226 | else: 227 | raise ValidationError({'parameter': '无效的测试数据'}) 228 | 229 | 230 | class Environment(models.Model): 231 | projectId = models.IntegerField(null=True) 232 | name = models.CharField(max_length=20, null=False) 233 | host = models.TextField(null=False) 234 | remark = models.TextField(null=True) 235 | 236 | class Meta: 237 | db_table = 'Environment' 238 | 239 | def clean(self): 240 | projectId = int(self.projectId) if str(self.projectId).isdigit() and int(self.projectId) > 0 else 0 241 | name = self.name.strip() if self.name else "" 242 | host = self.host.strip() if self.host else "" 243 | if projectId < 1: 244 | raise ValidationError({'projectId': '无效的项目Id'}) 245 | if not name or len(name) > 20 or len(name) < 1: 246 | raise ValidationError({'name': '无效的环境名称'}) 247 | if not host or len(host) < 1: 248 | raise ValidationError({'host': '无效的环境域名'}) 249 | 250 | 251 | class Browser(models.Model): 252 | name = models.CharField(max_length=20, null=False) 253 | value = models.CharField(max_length=20, null=False) 254 | remark = models.TextField(null=True) 255 | installPath = models.TextField(null=True) 256 | driverPath = models.TextField(null=True) 257 | 258 | class Meta: 259 | db_table = 'Browser' 260 | 261 | def clean(self): 262 | name = self.name.strip() if self.name else "" 263 | if 0 >= len(name) or len(name) > 20: 264 | raise ValidationError({'name': '无效的浏览器名称'}) 265 | value = self.value.strip() if self.value else "" 266 | if 0 >= len(value) or len(value) > 20: 267 | raise ValidationError({'value': '无效的浏览器控制器'}) 268 | 269 | def buid(self): 270 | browser = self.value.lower().strip() if self.value else "" 271 | if browser != 'android': 272 | from selenium import webdriver 273 | if browser == 'chrome': 274 | from selenium.webdriver.chrome.options import Options 275 | options = Options() 276 | options.add_argument('--headless') 277 | options.add_argument('--no-sandbox') 278 | options.add_argument('--disable-dev-shm-usage') 279 | browser = webdriver.Chrome(chrome_options=options) 280 | elif browser == 'firefox': 281 | browser = webdriver.Firefox() 282 | elif browser == 'edge': 283 | browser = webdriver.Edge() 284 | elif browser == 'ie': 285 | from selenium.webdriver.common.desired_capabilities import DesiredCapabilities 286 | DesiredCapabilities.INTERNETEXPLORER['ignoreProtectedModeSettings'] = True 287 | browser = webdriver.Ie() 288 | else: 289 | browser = webdriver.Chrome() 290 | browser.maximize_window() 291 | return browser 292 | else: 293 | from appium import webdriver 294 | desired_caps = { 295 | 296 | 'platformName': 'Android', 297 | 298 | 'platformVersion': '9', 299 | 300 | 'deviceName': '13b7cc66', 301 | 302 | 'appPackage': 'com.android.browser', 303 | 304 | 'appActivity': 'com.android.browser.BrowserActivity', 305 | 306 | "noReset": True, 307 | 308 | "noSign": True 309 | 310 | } 311 | browser = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) 312 | time.sleep(2) 313 | return browser 314 | 315 | class Result(models.Model): 316 | title = models.CharField(max_length=200, null=False) 317 | taskId = models.IntegerField(null=True, default=0) 318 | projectId = models.IntegerField() 319 | testcaseId = models.IntegerField() 320 | browsers = models.TextField(null=True) 321 | beforeLogin = models.TextField(null=True) 322 | environments = models.TextField(null=True) 323 | status = models.IntegerField(default=10) # 10 排队中 20 测试中 30 成功 40 失败 324 | parameter = models.TextField() 325 | steps = models.TextField(null=False) 326 | checkType = models.TextField() 327 | checkValue = models.TextField() 328 | checkText = models.TextField(null=True) 329 | selectText = models.TextField(null=True) 330 | createTime = models.DateTimeField(default=timezone.now) 331 | 332 | class Meta: 333 | db_table = 'Result' 334 | 335 | 336 | class SplitResult(models.Model): 337 | environmentId = models.IntegerField(null=True) 338 | browserId = models.IntegerField(null=True) 339 | resultId = models.IntegerField() 340 | loginStatus = models.IntegerField(default=0) # 1 成功 2 失败 3 跳过 341 | createTime = models.DateTimeField(default=timezone.now) 342 | startTime = models.DateTimeField(null=True) 343 | finishTime = models.DateTimeField(null=True) 344 | parameter = models.TextField() 345 | expect = models.BooleanField() 346 | status = models.IntegerField(default=10) # 10 排队中 20 测试中 30 成功 40 失败 50跳过 347 | remark = models.TextField(null=True) 348 | 349 | 350 | class Meta: 351 | db_table = 'SplitResult' 352 | 353 | 354 | class Task(models.Model): 355 | name = models.CharField(max_length=200, null=False) 356 | testcases = models.TextField(null=False) 357 | browsers = models.TextField(null=True) 358 | status = models.IntegerField(null=True, default=1) 359 | timing = models.IntegerField(null=False, default=1) # 1 定时 2 常规 360 | remark = models.TextField(null=True) 361 | createTime = models.DateTimeField(default=timezone.now) 362 | 363 | class Meta: 364 | db_table = 'Task' 365 | 366 | def clean(self): 367 | name = self.name.strip() if self.name else "" 368 | testcases = self.testcases if self.testcases else [] 369 | browsers = self.browsers if self.browsers else [] 370 | if not name or len(name) > 20 or len(name) < 1: 371 | raise ValidationError({'name': '无效的任务名称'}) 372 | if not (testcases and isinstance(testcases, list)): 373 | raise ValidationError({'testcases': '无效的测试用例集'}) 374 | if not (browsers and isinstance(browsers, list)): 375 | raise ValidationError({'browsers': '无效的浏览器设置'}) 376 | 377 | 378 | class Params: 379 | TYPE_STRING = 'string' 380 | TYPE_ELEMENT = 'element' 381 | TYPE_FILE = 'file' 382 | TYPES = [TYPE_ELEMENT, TYPE_FILE, TYPE_STRING] 383 | 384 | def __init__(self, kwargs): 385 | isParameter = kwargs.get("isParameter", False) 386 | Type = kwargs.get("type", None) 387 | key = kwargs.get("key", None) 388 | value = kwargs.get("value", None) 389 | if not (Type and isinstance(Type, str)): 390 | raise ValueError("Params object Type must be str type") 391 | Type = Type.lower() 392 | if Type not in Params.TYPES: 393 | raise ValueError("Params object Type value error") 394 | if isParameter and (not value or str(value).strip() == 0): 395 | raise ValueError("Params Type parameter must has key") 396 | else: 397 | self.Type = Type 398 | self.key = key.strip() 399 | self.value = value 400 | self.isParameter = isParameter 401 | 402 | def __dict__(self): 403 | obj = dict() 404 | obj["type"] = self.Type 405 | obj["isParameter"] = self.isParameter 406 | obj["value"] = self.value 407 | obj["key"] = self.key 408 | return obj 409 | 410 | 411 | class Check: 412 | TYPE_URL = 'url' 413 | TYPE_ELEMENT = 'element' 414 | TYPES = [TYPE_URL, TYPE_ELEMENT] 415 | 416 | def __init__(self, type_, value): 417 | self.type = type_ 418 | self.value = value 419 | if not (self.type and self.type in Check.TYPES): 420 | raise ValueError("Check对象的type属性值错误") 421 | if self.type and (value and not self.value.strip()): 422 | raise ValueError("Check对象的value属性不能为空") 423 | 424 | def __dict__(self): 425 | obj = dict() 426 | obj['type'] = self.type 427 | obj['value'] = self.value 428 | 429 | 430 | class LoginConfig(models.Model): 431 | projectId = models.IntegerField() 432 | name = models.CharField(max_length=20, null=False) 433 | remark = models.TextField(null=True) 434 | checkType = models.TextField(default='') 435 | checkValue = models.TextField(default='') 436 | checkText = models.TextField(default='') 437 | selectText = models.TextField(default='') 438 | steps = models.TextField(null=False) 439 | params = models.TextField() 440 | createTime = models.DateTimeField(default=timezone.now) 441 | 442 | class Meta: 443 | db_table = 'login' 444 | 445 | def clean(self): 446 | name = self.name.strip() if self.name else "" 447 | step = self.steps 448 | checkType = self.checkType 449 | checkValue = self.checkValue 450 | checkText = self.checkText 451 | selectText = self.selectText 452 | if not name or 0 >= len(name) or len(name) > 20: 453 | raise ValidationError({'name': '无效的登录配置名称'}) 454 | if not isinstance(step, list): 455 | raise ValidationError({'step': '无效的登录步骤 : steps'}) 456 | for s in step: 457 | if not isinstance(s, dict): 458 | raise ValidationError({'step': '无效的登录步骤 : step'}) 459 | if not "keywordId" in s: 460 | raise ValidationError({'step': '无效的登录步骤 : keywordId'}) 461 | keywordId = int(s.get("keywordId")) if str(s.get("keywordId")).isdigit() else 0 462 | if keywordId < 1: 463 | raise ValidationError({'step': '无效的登录步骤 : keywordId'}) 464 | if not ("values" in s and isinstance(s.get("values"), list)): 465 | raise ValidationError({'step': '无效的登录步骤 : values'}) 466 | values = s.get("values") 467 | for value in values: 468 | try: 469 | Params(value) 470 | except ValueError: 471 | raise ValidationError({'step': '无效的登录步骤 : value'}) 472 | try: 473 | Check(checkType, checkValue) 474 | except: 475 | raise ValidationError({'check': '无效的登录断言'}) 476 | 477 | 478 | class EnvironmentLogin(models.Model): 479 | loginId = models.IntegerField() 480 | environmentId = models.IntegerField() 481 | parameter = models.TextField() 482 | 483 | class Meta: 484 | db_table = 'EnvironmentLogin' 485 | 486 | 487 | 488 | -------------------------------------------------------------------------------- /Product/tasks.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | 4 | from celery.task import task 5 | 6 | 7 | # 自定义要执行的task任务 8 | 9 | 10 | @task 11 | def SplitTask(result_id): 12 | from Product.models import Result, SplitResult 13 | result = Result.objects.get(id=result_id) 14 | result.status = 20 15 | result.save() 16 | parameter = json.loads(result.parameter) if result.parameter else [] 17 | browsers = json.loads(result.browsers) if result.environments else [1] 18 | environments = json.loads(result.environments) if result.environments else [] 19 | for browser in browsers: 20 | if environments: 21 | for environmentId in environments: 22 | if parameter: 23 | for params in parameter: 24 | for k, v in params.items(): 25 | if v and isinstance(v, str): 26 | if '#time#' in v: 27 | v = v.replace('#time#', 28 | time.strftime('%Y%m%d', time.localtime(time.time()))) 29 | if '#random#' in v: 30 | import random 31 | v = v.replace('#random#', str(random.randint(1000, 9999))) 32 | if '#null#' == v: 33 | v = None 34 | if '#logo#' == v: 35 | v = "/home/Atp/logo.png" 36 | params[k] = v 37 | sr = SplitResult.objects.create(environmentId=environmentId, browserId=browser, 38 | resultId=result.id, 39 | parameter=json.dumps(params, ensure_ascii=False), 40 | expect=params.get('expect', True)) 41 | SplitTaskRunning.delay(sr.id) 42 | else: 43 | sr = SplitResult.objects.create(environmentId=environmentId, browserId=browser, resultId=result.id, 44 | parameter={}, expect=True) 45 | SplitTaskRunning.delay(sr.id) 46 | else: 47 | if parameter: 48 | for params in parameter: 49 | for k, v in params.items(): 50 | if v and isinstance(v, str): 51 | if '#time#' in v: 52 | v = v.replace('#time#', time.strftime('%Y%m%d', time.localtime(time.time()))) 53 | if '#random#' in v: 54 | import random 55 | v = v.replace('#random#', str(random.randint(1000, 9999))) 56 | if '#null#' == v: 57 | v = None 58 | if '#logo#' == v: 59 | v = "/home/Atp/logo.png" 60 | params[k] = v 61 | sr = SplitResult.objects.create(environmentId=0, browserId=browser, resultId=result.id, 62 | parameter=json.dumps(params, ensure_ascii=False), 63 | expect=params.get('expect', True)) 64 | SplitTaskRunning.delay(sr.id) 65 | else: 66 | sr = SplitResult.objects.create(environmentId=0, browserId=browser, resultId=result.id, 67 | parameter={}, expect=True) 68 | SplitTaskRunning.delay(sr.id) 69 | SplitTaskRan.delay(result_id) 70 | 71 | 72 | @task 73 | def SplitTaskRan(result_id): 74 | from Product.models import Result, SplitResult 75 | result = Result.objects.get(id=result_id) 76 | while len(SplitResult.objects.filter(resultId=result.id, status__in=[10, 20])) > 0: 77 | time.sleep(1) 78 | split_list = SplitResult.objects.filter(resultId=result.id) 79 | for split in split_list: 80 | expect = split.expect; 81 | result_ = True if split.status == 30 else False 82 | if expect != result_: 83 | result.status = 40 84 | result.save() 85 | return 86 | result.status = 30 87 | result.save() 88 | return 89 | 90 | 91 | @task 92 | def SplitTaskRunning(splitResult_id): 93 | from Product.models import SplitResult, Browser, Environment, Element, Check, Result, EnvironmentLogin, LoginConfig 94 | import django.utils.timezone as timezone 95 | from Autotest_platform.PageObject.Base import PageObject 96 | from Autotest_platform.helper.util import get_model 97 | split = SplitResult.objects.get(id=splitResult_id) 98 | result_ = Result.objects.get(id=split.resultId) 99 | steps = json.loads(result_.steps) if result_.steps else [] 100 | parameter = json.loads(split.parameter) if split.parameter else {} 101 | checkType = result_.checkType 102 | checkValue = result_.checkValue 103 | checkText = result_.checkText 104 | selectText = result_.selectText 105 | beforeLogin = json.loads(result_.beforeLogin) if result_.beforeLogin else [] 106 | split.status = 20 107 | split.save() 108 | split.startTime = timezone.now() 109 | environment = get_model(Environment, id=split.environmentId) 110 | host = environment.host if environment and environment.host else '' 111 | driver = None 112 | try: 113 | driver = Browser.objects.get(id=split.browserId).buid() 114 | except: 115 | split.status = 40 116 | split.remark = '浏览器初始化失败' 117 | split.finishTime = timezone.now() 118 | split.save() 119 | if driver: 120 | driver.quit() 121 | return 122 | if beforeLogin and len(beforeLogin) > 0: 123 | for bl in beforeLogin: 124 | login = get_model(LoginConfig, id=bl) 125 | loginCheckType = login.checkType 126 | loginCheckValue = login.checkValue 127 | loginCheckText = login.checkText 128 | loginSelectText = login.selectText 129 | if not login: 130 | split.loginStatus = 3 131 | split.status = 50 132 | split.remark = "找不到登陆配置,id=" + str(bl) 133 | split.finishTime = timezone.now() 134 | split.save() 135 | if driver: 136 | driver.quit() 137 | return 138 | loginSteps = json.loads(login.steps) if login.steps else [] 139 | loginParameter = {} 140 | if environment: 141 | environmentLogin = get_model(EnvironmentLogin, loginId=bl, environmentId=environment.id) 142 | if environmentLogin: 143 | loginParameter = json.loads(environmentLogin.parameter) if environmentLogin.parameter else {} 144 | for loginStep in loginSteps: 145 | try: 146 | Step(loginStep.get("keywordId"), loginStep.get("values")).perform(driver, loginParameter, host) 147 | except Exception as e: 148 | split.loginStatus = 2 149 | split.status = 50 150 | split.remark = "初始化登陆失败
登陆名称=" + login.name + " ,
错误信息=" + ("".join(e.args)) 151 | split.finishTime = timezone.now() 152 | split.save() 153 | if driver: 154 | driver.quit() 155 | return 156 | if loginCheckType: 157 | time.sleep(2) 158 | if loginCheckType == Check.TYPE_URL: 159 | if not driver.current_url.endswith(str(loginCheckValue)): 160 | split.loginStatus = 2 161 | split.status = 50 162 | split.remark = "初始化登陆失败
登陆名称=" + login.name + " ,
错误信息=登录断言不通过" 163 | split.finishTime = timezone.now() 164 | split.save() 165 | if driver: 166 | driver.quit() 167 | return 168 | elif loginCheckType == Check.TYPE_ELEMENT: 169 | element = loginCheckValue 170 | if str(loginCheckValue).isdigit(): 171 | element = get_model(Element, id=loginCheckValue) 172 | try: 173 | PageObject.find_element(driver, element) 174 | except: 175 | split.loginStatus = 2 176 | split.status = 50 177 | split.remark = "初始化登陆失败[ 登陆名称:" + login.name + " , 错误信息:断言不通过" 178 | split.finishTime = timezone.now() 179 | split.save() 180 | if driver: 181 | driver.quit() 182 | return 183 | else: 184 | split.loginStatus = 1 185 | index = 1 186 | for step in steps: 187 | try: 188 | Step(step.get("keywordId"), step.get("values")).perform(driver, parameter, host) 189 | index = index + 1 190 | except RuntimeError as re: 191 | split.status = 40 192 | split.remark = "测试用例执行第" + str(index) + "步失败,错误信息:" + str(re.args) 193 | split.finishTime = timezone.now() 194 | split.save() 195 | if driver: 196 | driver.quit() 197 | return 198 | except Exception as info: 199 | split.status = 40 200 | split.remark = "执行测试用例第" + str(index) + "步发生错误,请检查测试用例:" + str(info.args) 201 | split.finishTime = timezone.now() 202 | split.save() 203 | if driver: 204 | driver.quit() 205 | return 206 | remark = '测试用例未设置断言,建议设置' 207 | time.sleep(2) 208 | if checkType: 209 | if checkType == Check.TYPE_URL: 210 | TestResult = driver.current_url.endswith(checkValue) 211 | if not TestResult: 212 | if not split.expect: 213 | remark = '测试通过' 214 | else: 215 | remark = '测试不通过,预期结果为["' + checkValue + '"], 但实际结果为["' + driver.current_url + '"]' 216 | else: 217 | if split.expect: 218 | remark = '测试通过' 219 | else: 220 | remark = '测试不通过,预期结果为["' + checkValue + '"], 但实际结果为["' + driver.current_url + '"]' 221 | elif checkType == Check.TYPE_ELEMENT: 222 | element = checkValue 223 | expect_text = checkText 224 | select_text = selectText 225 | if str(checkValue).isdigit(): 226 | element = get_model(Element, id=int(element)) 227 | try: 228 | PageObject.find_element(driver, element) 229 | actual_text = PageObject.find_element(driver, element).text 230 | if select_text == 'all': 231 | if expect_text == actual_text: 232 | TestResult = True 233 | else: 234 | TestResult = False 235 | if TestResult: 236 | if split.expect: 237 | remark = '测试通过,预期断言值完全匹配实际断言值。' 238 | else: 239 | remark = '测试不通过,预期结果失败,但实际结果是成功。' 240 | else: 241 | if not split.expect: 242 | remark = '测试通过,预期结果失败,实际结果也是失败。' 243 | else: 244 | remark = '测试不通过,预期结果为["' + expect_text + '"],但实际结果为["' + actual_text + '"]' 245 | else: 246 | if expect_text in actual_text: 247 | TestResult = True 248 | else: 249 | TestResult = False 250 | if TestResult: 251 | if split.expect: 252 | remark = '测试通过,预期断言值包含匹配实际断言值。' 253 | else: 254 | remark = '测试不通过,预期结果失败,但实际结果是成功。' 255 | else: 256 | if not split.expect: 257 | remark = '测试通过,预期结果失败,实际结果也是失败。' 258 | else: 259 | remark = '测试不通过,预期结果为["' + expect_text + '"],但实际结果为["' + actual_text + '"]' 260 | except: 261 | TestResult = False 262 | remark = '当前元素定位已改变,请及时更新定位!' 263 | 264 | 265 | 266 | if driver: 267 | driver.quit() 268 | split.status = 30 if TestResult else 40 269 | split.remark = remark 270 | split.finishTime = timezone.now() 271 | split.save() 272 | return 273 | 274 | 275 | @task 276 | def timingRunning(): 277 | from Product.models import Task, TestCase, Result 278 | from Autotest_platform.helper.util import get_model 279 | tasks = Task.objects.filter(timing=1) 280 | for t in tasks: 281 | browsers = json.loads(t.browsers) if t.browsers else [] 282 | testcases = json.loads(t.testcases) if t.testcases else [] 283 | for tc in testcases: 284 | environments = tc.get("environments", []) 285 | tc = get_model(TestCase, id=tc.get("id", 0)) 286 | r = Result.objects.create(projectId=tc.projectId, testcaseId=tc.id, checkValue=tc.checkValue, 287 | checkType=tc.checkType, checkText=tc.checkText, selectText=tc.selectText, 288 | title=tc.title, beforeLogin=tc.beforeLogin, 289 | steps=tc.steps, parameter=tc.parameter, 290 | browsers=json.dumps(browsers, ensure_ascii=False), 291 | environments=json.dumps(environments, ensure_ascii=False), taskId=t.id) 292 | SplitTask.delay(r.id) 293 | 294 | 295 | class Step: 296 | def __init__(self, keyword_id, values): 297 | from .models import Keyword, Params 298 | from Autotest_platform.helper.util import get_model 299 | self.keyword = get_model(Keyword, id=keyword_id) 300 | self.params = [Params(value) for value in values] 301 | 302 | def perform(self, driver, parameter, host): 303 | from .models import Params, Element 304 | if self.keyword.type == 1: 305 | values = list() 306 | for p in self.params: 307 | if p.isParameter: 308 | if p.Type == Params.TYPE_ELEMENT: 309 | v = Element.objects.get(id=parameter.get(p.value, None)) 310 | else: 311 | v = parameter.get(p.value, None) 312 | elif p.Type == Params.TYPE_ELEMENT: 313 | v = Element.objects.get(id=p.value) 314 | else: 315 | v = p.value 316 | if self.keyword.method == 'open_url' and not ('http://' in v or 'https://' in v): 317 | v = host + v 318 | values.append(v) 319 | try: 320 | self.sys_method__run(driver, tuple(values)) 321 | except: 322 | raise 323 | elif self.keyword.type == 2: 324 | steps = json.loads(self.keyword.steps) 325 | for pa in self.params: 326 | if not pa.isParameter: 327 | if pa.Type == Params.TYPE_ELEMENT: 328 | parameter[pa.key] = Element.objects.get(id=pa.value) 329 | else: 330 | parameter[pa.key] = pa.value 331 | for step in steps: 332 | try: 333 | Step(step.get("keywordId"), step.get("values")).perform(driver, parameter, host) 334 | except: 335 | raise 336 | 337 | def sys_method__run(self, driver, value): 338 | package = __import__(self.keyword.package, fromlist=True) 339 | clazz = getattr(package, self.keyword.clazz) 340 | setattr(clazz, "driver", driver) 341 | method = getattr(clazz, self.keyword.method) 342 | 343 | def running(*args): 344 | try: 345 | c = clazz() 346 | para = (c,) 347 | args = para + args[0] 348 | method(*args) 349 | except: 350 | raise 351 | 352 | try: 353 | running(value) 354 | except: 355 | raise -------------------------------------------------------------------------------- /Product/tests.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | 3 | 4 | webdriver.Chrome() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Autotest_platform 2 | 3 | Autotest_platform 是一款基于 POM 模式开发的 Web UI 自动化测试平台,通过选择绑定好的 selenium 的关键字,比如打开 url,左键点击,输入文本等等这些动作,背后的代码已在后端封装好,前端只需要像填表格来设计测试案例即可。此外,由于平台是基于 POM 模式的设计,项目,页面,元素,定位都可拆分管理,我们可以更为方便维护我们的测试脚本,当产品发生迭代的时候,我们只需要修改对应页面对应元素的定位即可,无需重新设计涉及到这个元素的所有测试案例。数据库当中已封装了 selenium 常见的关键字,可以在 ./Autotest_platform/PageObject/Base.py 里面自行添加自己需要的 selenium 关键字,甚至是更为复杂的自定义函数。./Autotest_platform/PageObject/Base_m.py 为手机端函数的封装,需要开启 Appium 连接手机。 4 | 5 | 平台采用 RabbitMQ + celery 的方式执行测试案例,支持异步分布执行测试案例,也支持开启定时任务。 6 | 7 | 平台构建相关技术栈:python + django + mysql + RabbitMQ + celery + selenium 8 | 9 | 平台后端 POM 原理层脚本 Demo 展示:[pom_autotest](https://github.com/ShaoNianyr/pom_autotest) 10 | 11 | ## 平台功能展示 12 | 1. 首页用图标展示项目以及测试汇总的数据: 13 | 14 | 15 | 16 | 2. 测试案例可分项目管理,多个测试项目之间均可通用 "selenium关键字封装"项目 封装好的关键字: 17 | 18 | 19 | 20 | 3. 采用 POM 模式设计,每个项目对应管理多个页面,每个页面对应管理多个页面元素的定位: 21 | 22 | 23 | 24 | 25 | 4. selenium 的动作也从测试案例中单独抽出来,作为一个函数来调用,同一个动作可以供多个测试案例一起使用,比如每个测试案例里面的输入文本的动作,都可以直接调用这个 "输入文本" 的关键字,关键字封装在 ./Autotest_platform/PageObject/Base.py 里面,对外暴露出两个参数,也就是输入的定位,以及输入的内容,设计测试案例的时候只需要对应填入这两个参数,它就会自动执行对应的 selenium 语句。 Appium 对应的关键字封装在 ./Autotest_platform/PageObject/Base_m.py 里面。 26 | 27 | 28 | 29 | 30 | 5. 支持浏览器有头执行案例,也支持浏览器无头执行案例,便于调试。也支持对手机端测试案例的执行,需开启 Appium 以及 adb 连接到手机,获取你要打开的应用和手机设备型号,并在 ./Product/models/models.py 如下代码的对应位置修改填写: 31 | from appium import webdriver 32 | desired_caps = { 33 | 34 | 'platformName': 'Android', 35 | 36 | 'platformVersion': '9', 37 | 38 | 'deviceName': '13b7cc66', 39 | 40 | 'appPackage': 'com.android.browser', 41 | 42 | 'appActivity': 'com.android.browser.BrowserActivity', 43 | 44 | "noReset": True, 45 | 46 | "noSign": True 47 | 48 | } 49 | browser = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) 50 | time.sleep(2) 51 | return browser 52 | 53 | 54 | 6. 支持一键快速复制测试案例: 55 | 56 | 57 | 58 | 7. 支持断言 url 以及 元素两种方式,支持完全匹配和包含匹配两种断言力度,支持测试案例设计过程中的参数化设计: 59 | 60 | 61 | 62 | 8. 支持测试案例的前置登录操作,可以在登录配置当中设置: 63 | 64 | 65 | 66 | 9. 生成的测试结果中可展示多种详细的信息报告: 67 | 68 | 69 | 70 | 10. 支持选择任意的测试案例构建测试集合,支持定时任务测试集合构建: 71 | 72 | 73 | 74 | ## Windows 安装部署 75 | 76 | ### 1. 获取项目代码 77 | 78 | 首先在 d 盘根目录获取源代码,然后切换至项目的根目录: 79 | (如果代码不在 d 盘根目录,后续的相关路径请按照自己的路径来修改,此处以 d 盘根目录为例部署) 80 | 81 | ```bash 82 | cd d: 83 | git clone https://github.com/ShaoNianyr/Autotest_platform.git 84 | cd Autotest_platform 85 | ``` 86 | ### 2. python3.6 pip 包安装 87 | 此处要先安装好 python3.6 环境,由于我有多个版本,所以设置的 python3.6 的环境变量为 python36。 88 | 89 | ```bash 90 | python36 -m pip install -r requirements.txt 91 | ``` 92 | 如果你没有设置 pip 镜像,建议执行如下安装 pip 包指令: 93 | 94 | ```bash 95 | python36 -m pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com 96 | ``` 97 | 98 | 99 | ### 3. Mysql5.7 安装: 100 | 这里我提供一个 docker-compose.yml 文件,可以帮你快速构建平台所需的 Mysql 数据库镜像,初始化设置并本地挂载数据库的内容。 101 | 102 | 首先安装 Docker Desktop,并在设置里面勾选 d 盘允许数据挂载。 103 | 104 | 105 | 106 | 然后执行指令: 107 | 108 | ```bash 109 | docker-compose up 110 | ``` 111 | 112 | 如果本地有安装到 Mysql 占用了端口导致启动失败,可以先执行指令杀死端口。 113 | 114 | ```bash 115 | netstat -ano|findstr "3306" 116 | taskkill /pid xxxx -t -f (xxxx 为你 3306 端口对应的pid) 117 | docker-compose up 118 | ``` 119 | 120 | 一切正常,最终可以看到: 121 | 122 | 123 | 124 | 接下来我们可以安装 Navicat Premium 12,连接我们的 docker mysql 镜像,将 autotest.sql 文件导入。当然你也可以直接手写 sql 语句建库导入。 125 | 126 | 在 Navicat Premium 12 中新建连接,如图所示设置: 127 | 128 | 129 | 130 | 点开我们的数据库,右键然后运行文件,执行如下设置,并导入 autotest.sql: 131 | 132 | 133 | 134 | 135 | 136 | 成功以后,我们的数据库就具备 Demo 的数据在里面了。 137 | 138 | 如果想用本地 Mysql,新建如下的数据库名,并将代码修改你的 Mysql 的密码,再把 sql 文件导入即可。 139 | 140 | 141 | 142 | 代码位置: ./Autotest_platform/settings.py 143 | 144 | ### 4. RabbitMQ 安装: 145 | - [RabbitMQ下载与安装(window版)](https://www.jianshu.com/p/3d43561bb3ee) 146 | 147 | 安装完毕以后,浏览器输入 http://localhost:15672 ,输入用户名:guest,密码:guest,你就可以进入到测试案例执行的消息队列的管理界面。 148 | 149 | ### 5. 启动 Django 项目: 150 | 151 | 因为之前已经导入过 sql 文件,就不需要再执行 python36 manage.py makesmigrations 这些指令,表格和数据都有了,所以直接执行: 152 | ```bash 153 | python36 manage.py runserver 154 | ``` 155 | 156 | 打开网址 http://127.0.0.1:8000/login/ ,输入账号和密码: 157 | ```bash 158 | 初始用户名: 少年 159 | 初始密码: sn123456 160 | ``` 161 | 接下来即可顺利进入到首页: 162 | 163 | 164 | 账户的管理在 Django 后台 http://127.0.0.1:8000/admin/ 里面: 165 | 166 | 167 | ### 6. 启动 celery 异步执行测试案例: 168 | 169 | 当你点击执行案例的时候,平台会将测试案例放到消息队列当中等待执行,等你开启 celery 的 worker 模式时,才会开始执行命令。具体执行如下指令:(注意要处于项目根目录下) 170 | ```bash 171 | python36 manage.py celeryd -l info 172 | ``` 173 | 174 | 175 | ### 7. 启动 celery 定时执行测试案例: 176 | ```bash 177 | python36 manage.py celerybeat -l info 178 | ``` 179 | 定时时间在 ./Autotest_platform/settings.py 中设置: 180 | ```bash 181 | CELERYBEAT_SCHEDULE = { 182 | 'timing': { 183 | 'task': 'Product.tasks.timingRunning', 184 | # 'schedule': crontab(hour=10, minute=30), 185 | 'schedule': timedelta(seconds=300), 186 | }, 187 | } 188 | ``` 189 | 190 | # 贡献 191 | 192 | - [ATP - UI 自动化测试用例管理平台](https://testerhome.com/topics/14676) -------------------------------------------------------------------------------- /__pycache__/manage.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/__pycache__/manage.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/manage.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/__pycache__/manage.cpython-37.pyc -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | mysql: 5 | image: mysql:5.7 6 | command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --init-connect='SET NAMES utf8mb4;' --innodb-flush-log-at-trx-commit=0 7 | volumes: 8 | - d:/Autotest_platform/mysql:/var/lib/mysql 9 | ports: 10 | - "3306:3306" 11 | environment: 12 | - MYSQL_ROOT_PASSWORD=123456 13 | - MYSQL_DATABASE=autotest -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Autotest_platform.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /pictures/assertValue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/assertValue.png -------------------------------------------------------------------------------- /pictures/checkElementParameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/checkElementParameters.png -------------------------------------------------------------------------------- /pictures/codeDiff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/codeDiff.png -------------------------------------------------------------------------------- /pictures/copyTesecase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/copyTesecase.png -------------------------------------------------------------------------------- /pictures/djangoSettingDatabase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/djangoSettingDatabase.png -------------------------------------------------------------------------------- /pictures/djangoUsersManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/djangoUsersManager.png -------------------------------------------------------------------------------- /pictures/dockerMysql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/dockerMysql.png -------------------------------------------------------------------------------- /pictures/dockerSharedDrives.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/dockerSharedDrives.png -------------------------------------------------------------------------------- /pictures/elementManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/elementManager.png -------------------------------------------------------------------------------- /pictures/improvedVersion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/improvedVersion.png -------------------------------------------------------------------------------- /pictures/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/index.png -------------------------------------------------------------------------------- /pictures/initDatabase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/initDatabase.png -------------------------------------------------------------------------------- /pictures/installPyRequirements.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/installPyRequirements.png -------------------------------------------------------------------------------- /pictures/linkDockerMysql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/linkDockerMysql.png -------------------------------------------------------------------------------- /pictures/linuxCelery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/linuxCelery.png -------------------------------------------------------------------------------- /pictures/loginPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/loginPage.png -------------------------------------------------------------------------------- /pictures/loginSetting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/loginSetting.png -------------------------------------------------------------------------------- /pictures/pageManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/pageManager.png -------------------------------------------------------------------------------- /pictures/projectManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/projectManager.png -------------------------------------------------------------------------------- /pictures/runCelery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/runCelery.png -------------------------------------------------------------------------------- /pictures/runSqlFile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/runSqlFile.png -------------------------------------------------------------------------------- /pictures/runSqlFileSetting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/runSqlFileSetting.png -------------------------------------------------------------------------------- /pictures/selectAutotestDatabase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/selectAutotestDatabase.png -------------------------------------------------------------------------------- /pictures/seleniumManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/seleniumManager.png -------------------------------------------------------------------------------- /pictures/setSelenium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/setSelenium.png -------------------------------------------------------------------------------- /pictures/testReport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/testReport.png -------------------------------------------------------------------------------- /pictures/timeSetting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/pictures/timeSetting.png -------------------------------------------------------------------------------- /pip.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | index-url = http://mirrors.aliyun.com/pypi/simple/ 3 | [install] 4 | trusted-host=mirrors.aliyun.com -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | amqp==1.4.9 2 | anyjson==0.3.3 3 | billiard==3.3.0.23 4 | celery==3.1.26.post2 5 | certifi==2019.6.16 6 | chardet==3.0.4 7 | Django==2.1.11 8 | django-celery==3.3.0 9 | django-celery-beat==1.5.0 10 | django-timezone-field==3.0 11 | framework==0.1.0 12 | get==2019.4.13 13 | idna==2.8 14 | kombu==3.0.37 15 | post==2019.4.13 16 | public==2019.4.13 17 | PyMySQL==0.9.3 18 | python-crontab==2.3.7 19 | python-dateutil==2.8.0 20 | pytz==2019.1 21 | query-string==2019.4.13 22 | request==2019.4.13 23 | requests==2.22.0 24 | selenium==3.141.0 25 | six==1.12.0 26 | sqlparse==0.3.0 27 | urllib3==1.25.3 28 | vine==1.3.0 29 | -------------------------------------------------------------------------------- /static/assets/css/custom-styles.css: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------- 2 | COMMON STYLES 3 | ------------------------------------------------*/ 4 | body { 5 | font-family: 'Open Sans', sans-serif; 6 | } 7 | 8 | #wrapper { 9 | width: 100%; 10 | background:#09192A; 11 | } 12 | 13 | #page-wrapper { 14 | padding: 15px 15px; 15 | min-height: 600px; 16 | background:#E5EBF2; 17 | 18 | } 19 | #page-inner { 20 | width:100%; 21 | margin:10px 20px 10px 0px; 22 | background-color:transparent; 23 | padding:10px; 24 | min-height:1200px; 25 | } 26 | 27 | .mar-rignt-20{ margin-right:20px} 28 | .mar-rignt-100{ margin-right:100px} 29 | 30 | .mar-bottom-20{ margin-bottom:20px} 31 | 32 | .mar-top-6{ margin-top:6px} 33 | 34 | .width-80{ width:80px} 35 | .width-200{ width:200px} 36 | .width-p8{ width:80%} 37 | .width-p9{ width:90%} 38 | .dataTables_paginate{ text-align:right} 39 | .f-c:before, .f-c:after {content:""; display:table;} .f-c:after {clear:both; }.f-c { zoom:1;} 40 | 41 | 42 | .color-white{ color:#fff} 43 | .color-red{ color:#F30} 44 | .color-green{ color:#093} 45 | .pointer{ cursor:pointer} 46 | .text-center { 47 | text-align:center; 48 | } 49 | .no-boder { 50 | border:1px solid #f3f3f3; 51 | } 52 | 53 | h1, .h1, h2, .h2, h3, .h3 { 54 | margin-top: 7px; 55 | margin-bottom: -5px; 56 | } 57 | h2 { 58 | color: #000; 59 | } 60 | h4 { 61 | padding-top:10px; 62 | } 63 | .square-btn-adjust { 64 | border: 0px solid transparent; 65 | -webkit-border-radius: 0px; 66 | -moz-border-radius: 0px; 67 | border-radius: 0px; 68 | 69 | } 70 | p { 71 | font-size:16px; 72 | line-height:25px; 73 | padding-top:20px; 74 | } 75 | /*---------------------------------------------- 76 | DASHBOARD STYLES 77 | ------------------------------------------------*/ 78 | .page-header { 79 | padding-bottom: 9px; 80 | margin: 0px 0 10px; 81 | border-bottom: 1px solid #C7D1DD; 82 | font-size:18px; 83 | } 84 | .panel-back { 85 | background-color:#fff; 86 | 87 | } 88 | .panel-default > .panel-heading { 89 | color: #000; 90 | background-color: #FFFFFF; 91 | border-color: #ddd; 92 | font-weight:bold; 93 | } 94 | .jumbotron, .well{ 95 | background:#fff; 96 | } 97 | .noti-box { 98 | min-height: 100px; 99 | padding: 20px; 100 | } 101 | 102 | .noti-box .icon-box { 103 | display: block; 104 | float: left; 105 | margin: 0 15px 10px 0; 106 | width: 70px; 107 | height: 70px; 108 | line-height: 75px; 109 | vertical-align: middle; 110 | text-align: center; 111 | font-size: 40px; 112 | } 113 | .text-box p{ 114 | margin: 0 0 3px; 115 | } 116 | .main-text { 117 | font-size: 25px; 118 | font-weight:600; 119 | } 120 | .set-icon { 121 | -webkit-border-radius: 50px; 122 | -moz-border-radius: 50px; 123 | border-radius: 50px; 124 | 125 | } 126 | .bg-color-green { 127 | background-color: #fff; 128 | color: #5cb85c; 129 | } 130 | .bg-color-blue { 131 | background-color: #fff; 132 | color: #4CB1CF 133 | } 134 | .bg-color-red { 135 | background-color: #fff; 136 | color:#F0433D; 137 | } 138 | .bg-color-brown { 139 | background-color: #fff; 140 | color:#f0ad4e; 141 | } 142 | .back-footer-green { 143 | background-color: #5cb85c; 144 | color:#fff; 145 | border-top: 0px solid #fff; 146 | } 147 | .back-footer-red { 148 | background-color: #F0433D; 149 | color:#fff; 150 | border-top: 0px solid #fff; 151 | } 152 | .back-footer-blue { 153 | background-color: #4CB1CF; 154 | color:#fff; 155 | border-top: 0px solid #fff; 156 | } 157 | .back-footer-brown { 158 | background-color: #f0ad4e; 159 | color:#fff; 160 | border-top: 0px solid #fff; 161 | } 162 | .icon-box-right { 163 | display: block; 164 | float: right; 165 | margin: 0 15px 10px 0; 166 | width: 70px; 167 | height: 70px; 168 | line-height: 75px; 169 | vertical-align: middle; 170 | text-align: center; 171 | font-size: 40px; 172 | } 173 | 174 | .main-temp-back { 175 | background: #8702A8; 176 | color: #FFFFFF; 177 | font-size: 16px; 178 | font-weight: 300; 179 | text-align: center; 180 | } 181 | .main-temp-back .text-temp { 182 | font-size: 40px; 183 | } 184 | .back-dash { 185 | padding:20px; 186 | font-size:20px; 187 | font-weight:500; 188 | -webkit-border-radius: 0px; 189 | -moz-border-radius: 0px; 190 | border-radius: 0px; 191 | background-color:#2EA7EB; 192 | color:#fff; 193 | } 194 | .back-dash p { 195 | padding-top:16px; 196 | font-size:13px; 197 | color:#fff; 198 | line-height:25px; 199 | text-align:justify; 200 | } 201 | 202 | .color-bottom-txt { 203 | color: #000; 204 | font-size: 16px; 205 | line-height: 30px; 206 | } 207 | /*CHAT PANEL*/ 208 | .chat-panel .panel-body { 209 | height: 450px; 210 | overflow-y: scroll; 211 | } 212 | .chat-box { 213 | margin: 0; 214 | padding: 0; 215 | list-style: none; 216 | } 217 | .chat-box li { 218 | margin-bottom: 15px; 219 | padding-bottom: 5px; 220 | border-bottom: 1px dotted #808080; 221 | } 222 | .chat-box li.left .chat-body { 223 | margin-left: 90px; 224 | } 225 | .chat-box li .chat-body p { 226 | margin: 0; 227 | color: #8d8888; 228 | } 229 | .chat-img>img { 230 | margin-left:20px; 231 | } 232 | footer p{ 233 | font-size: 14px; 234 | } 235 | /*---------------------------------------------- 236 | MENU STYLES 237 | ------------------------------------------------*/ 238 | 239 | 240 | .user-image { 241 | margin: 25px auto; 242 | -webkit-border-radius: 10px; 243 | -moz-border-radius: 10px; 244 | border-radius: 10px; 245 | max-height:170px; 246 | max-width:170px; 247 | } 248 | .top-navbar{ 249 | margin:0px; 250 | } 251 | .top-navbar .navbar-brand { 252 | color: #fff; 253 | width: 260px; 254 | text-align: left; 255 | height: 60px; 256 | font-size: 21px; 257 | font-weight: 700; 258 | text-transform: uppercase; 259 | line-height: 30px; 260 | } 261 | .top-navbar .nav > li { 262 | position: relative; 263 | display: inline-block; 264 | } 265 | .top-navbar .nav > li > a { 266 | position: relative; 267 | display: block; 268 | padding: 19px 15px; 269 | color: #77C0FD; 270 | } 271 | .top-navbar .nav > li > a:hover, .top-navbar .nav > li > a:focus { 272 | text-decoration: none; 273 | background-color: #225081; 274 | color: #fff; 275 | } 276 | .top-navbar .dropdown-menu{ 277 | min-width: 230px; 278 | border-radius: 0 0 4px 4px; 279 | } 280 | .top-navbar .dropdown-menu > li > a:hover, .top-navbar .dropdown-menu > li > a:focus{ 281 | color: #225081; 282 | background:none; 283 | } 284 | .dropdown-tasks{ 285 | width: 255px; 286 | } 287 | .dropdown-tasks .progress { 288 | height: 8px; 289 | margin-bottom: 8px; 290 | overflow: hidden; 291 | background-color: #f5f5f5; 292 | border-radius: 0px; 293 | } 294 | .dropdown-tasks > li > a { 295 | padding: 0px 15px; 296 | } 297 | .dropdown-tasks p { 298 | font-size: 13px; 299 | line-height: 21px; 300 | padding-top: 4px; 301 | } 302 | .active-menu { 303 | background-color:#225081!important; 304 | } 305 | 306 | .arrow { 307 | float: right; 308 | } 309 | 310 | .fa.arrow:before { 311 | content: "\f104"; 312 | } 313 | 314 | .active > a > .fa.arrow:before { 315 | content: "\f107"; 316 | } 317 | 318 | 319 | .nav-second-level li, 320 | .nav-third-level li { 321 | border-bottom: none !important; 322 | } 323 | 324 | .nav-second-level li a { 325 | padding-left: 37px; 326 | } 327 | 328 | .nav-third-level li a { 329 | padding-left: 55px; 330 | } 331 | .sidebar-collapse , .sidebar-collapse .nav{ 332 | background:none; 333 | } 334 | .sidebar-collapse .nav { 335 | padding:0; 336 | } 337 | .sidebar-collapse .nav > li > a { 338 | color:#fff; 339 | background:transparent; 340 | text-shadow:none; 341 | 342 | } 343 | .sidebar-collapse > .nav > li > a { 344 | padding:15px 10px 15px 30px; 345 | } 346 | .sidebar-collapse > .nav > li { 347 | border-bottom: 1px solid rgba(107, 108, 109, 0.19); 348 | } 349 | ul.nav.nav-second-level.collapse.in { 350 | background: #172D44; 351 | } 352 | .sidebar-collapse .nav > li > a:hover, 353 | .sidebar-collapse .nav > li > a:focus { 354 | 355 | outline:0; 356 | } 357 | 358 | .navbar-side { 359 | border:none; 360 | background-color: transparent; 361 | 362 | } 363 | .top-navbar { 364 | background:#09192A; 365 | border-bottom:none; 366 | 367 | } 368 | .top-navbar .nav > li > a > i { 369 | margin-right: 2px; 370 | } 371 | .top-navbar .navbar-brand:hover { 372 | color:#fff; 373 | 374 | } 375 | .dropdown-user li { 376 | margin: 8px 0; 377 | } 378 | .navbar-default { 379 | border:0px solid black; 380 | 381 | } 382 | .navbar-header { 383 | background: #09192A; 384 | } 385 | .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { 386 | background-color: #B40101; 387 | } 388 | .navbar-default .navbar-toggle { 389 | border-color: #fff; 390 | } 391 | 392 | .navbar-default .navbar-toggle .icon-bar { 393 | background-color: #FFF; 394 | } 395 | .nav > li > a > i { 396 | margin-right:10px; 397 | } 398 | /*---------------------------------------------- 399 | UI ELEMENTS STYLES 400 | ------------------------------------------------*/ 401 | .btn-circle { 402 | width: 50px; 403 | height: 50px; 404 | padding: 6px 0; 405 | -webkit-border-radius: 25px; 406 | -moz-border-radius: 25px; 407 | border-radius: 25px; 408 | text-align: center; 409 | font-size: 12px; 410 | line-height: 1.428571429; 411 | } 412 | 413 | /*---------------------------------------------- 414 | MEDIA QUERIES 415 | ------------------------------------------------*/ 416 | 417 | @media(min-width:768px) { 418 | #page-wrapper{ 419 | margin: 0 0 0 200px; 420 | padding: 15px 30px; 421 | min-height: 1200px; 422 | 423 | } 424 | 425 | 426 | .navbar-side { 427 | z-index: 1; 428 | position: absolute; 429 | width: 200px; 430 | } 431 | 432 | .navbar { 433 | border-radius: 0px; 434 | } 435 | 436 | } 437 | @media(max-width:480px) { 438 | .page-header small { 439 | display: block; 440 | padding-top: 14px; 441 | font-size: 19px; 442 | } 443 | } 444 | 445 | 446 | -------------------------------------------------------------------------------- /static/assets/datetimepicker/bootstrap-datetimepicker.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Datetimepicker for Bootstrap 3 | * 4 | * Copyright 2012 Stefan Petre 5 | * Improvements by Andrew Rowls 6 | * Licensed under the Apache License v2.0 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | */.datetimepicker{padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;direction:ltr}.datetimepicker-inline{width:220px}.datetimepicker.datetimepicker-rtl{direction:rtl}.datetimepicker.datetimepicker-rtl table tr td span{float:right}.datetimepicker-dropdown,.datetimepicker-dropdown-left{top:0;left:0}[class*=" datetimepicker-dropdown"]:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,0.2);position:absolute}[class*=" datetimepicker-dropdown"]:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute}[class*=" datetimepicker-dropdown-top"]:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,0.2);border-bottom:0}[class*=" datetimepicker-dropdown-top"]:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;border-bottom:0}.datetimepicker-dropdown-bottom-left:before{top:-7px;right:6px}.datetimepicker-dropdown-bottom-left:after{top:-6px;right:7px}.datetimepicker-dropdown-bottom-right:before{top:-7px;left:6px}.datetimepicker-dropdown-bottom-right:after{top:-6px;left:7px}.datetimepicker-dropdown-top-left:before{bottom:-7px;right:6px}.datetimepicker-dropdown-top-left:after{bottom:-6px;right:7px}.datetimepicker-dropdown-top-right:before{bottom:-7px;left:6px}.datetimepicker-dropdown-top-right:after{bottom:-6px;left:7px}.datetimepicker>div{display:none}.datetimepicker.minutes div.datetimepicker-minutes{display:block}.datetimepicker.hours div.datetimepicker-hours{display:block}.datetimepicker.days div.datetimepicker-days{display:block}.datetimepicker.months div.datetimepicker-months{display:block}.datetimepicker.years div.datetimepicker-years{display:block}.datetimepicker table{margin:0}.datetimepicker td,.datetimepicker th{text-align:center;width:20px;height:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:0}.table-striped .datetimepicker table tr td,.table-striped .datetimepicker table tr th{background-color:transparent}.datetimepicker table tr td.minute:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.hour:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.day:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.old,.datetimepicker table tr td.new{color:#999}.datetimepicker table tr td.disabled,.datetimepicker table tr td.disabled:hover{background:0;color:#999;cursor:default}.datetimepicker table tr td.today,.datetimepicker table tr td.today:hover,.datetimepicker table tr td.today.disabled,.datetimepicker table tr td.today.disabled:hover{background-color:#fde19a;background-image:-moz-linear-gradient(top,#fdd49a,#fdf59a);background-image:-ms-linear-gradient(top,#fdd49a,#fdf59a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdd49a),to(#fdf59a));background-image:-webkit-linear-gradient(top,#fdd49a,#fdf59a);background-image:-o-linear-gradient(top,#fdd49a,#fdf59a);background-image:linear-gradient(to bottom,#fdd49a,#fdf59a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a',endColorstr='#fdf59a',GradientType=0);border-color:#fdf59a #fdf59a #fbed50;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.datetimepicker table tr td.today:hover,.datetimepicker table tr td.today:hover:hover,.datetimepicker table tr td.today.disabled:hover,.datetimepicker table tr td.today.disabled:hover:hover,.datetimepicker table tr td.today:active,.datetimepicker table tr td.today:hover:active,.datetimepicker table tr td.today.disabled:active,.datetimepicker table tr td.today.disabled:hover:active,.datetimepicker table tr td.today.active,.datetimepicker table tr td.today:hover.active,.datetimepicker table tr td.today.disabled.active,.datetimepicker table tr td.today.disabled:hover.active,.datetimepicker table tr td.today.disabled,.datetimepicker table tr td.today:hover.disabled,.datetimepicker table tr td.today.disabled.disabled,.datetimepicker table tr td.today.disabled:hover.disabled,.datetimepicker table tr td.today[disabled],.datetimepicker table tr td.today:hover[disabled],.datetimepicker table tr td.today.disabled[disabled],.datetimepicker table tr td.today.disabled:hover[disabled]{background-color:#fdf59a}.datetimepicker table tr td.today:active,.datetimepicker table tr td.today:hover:active,.datetimepicker table tr td.today.disabled:active,.datetimepicker table tr td.today.disabled:hover:active,.datetimepicker table tr td.today.active,.datetimepicker table tr td.today:hover.active,.datetimepicker table tr td.today.disabled.active,.datetimepicker table tr td.today.disabled:hover.active{background-color:#fbf069}.datetimepicker table tr td.active,.datetimepicker table tr td.active:hover,.datetimepicker table tr td.active.disabled,.datetimepicker table tr td.active.disabled:hover{background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-ms-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc',endColorstr='#0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.datetimepicker table tr td.active:hover,.datetimepicker table tr td.active:hover:hover,.datetimepicker table tr td.active.disabled:hover,.datetimepicker table tr td.active.disabled:hover:hover,.datetimepicker table tr td.active:active,.datetimepicker table tr td.active:hover:active,.datetimepicker table tr td.active.disabled:active,.datetimepicker table tr td.active.disabled:hover:active,.datetimepicker table tr td.active.active,.datetimepicker table tr td.active:hover.active,.datetimepicker table tr td.active.disabled.active,.datetimepicker table tr td.active.disabled:hover.active,.datetimepicker table tr td.active.disabled,.datetimepicker table tr td.active:hover.disabled,.datetimepicker table tr td.active.disabled.disabled,.datetimepicker table tr td.active.disabled:hover.disabled,.datetimepicker table tr td.active[disabled],.datetimepicker table tr td.active:hover[disabled],.datetimepicker table tr td.active.disabled[disabled],.datetimepicker table tr td.active.disabled:hover[disabled]{background-color:#04c}.datetimepicker table tr td.active:active,.datetimepicker table tr td.active:hover:active,.datetimepicker table tr td.active.disabled:active,.datetimepicker table tr td.active.disabled:hover:active,.datetimepicker table tr td.active.active,.datetimepicker table tr td.active:hover.active,.datetimepicker table tr td.active.disabled.active,.datetimepicker table tr td.active.disabled:hover.active{background-color:#039}.datetimepicker table tr td span{display:block;width:23%;height:54px;line-height:54px;float:left;margin:1%;cursor:pointer;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.datetimepicker .datetimepicker-hours span{height:26px;line-height:26px}.datetimepicker .datetimepicker-hours table tr td span.hour_am,.datetimepicker .datetimepicker-hours table tr td span.hour_pm{width:14.6%}.datetimepicker .datetimepicker-hours fieldset legend,.datetimepicker .datetimepicker-minutes fieldset legend{margin-bottom:inherit;line-height:30px}.datetimepicker .datetimepicker-minutes span{height:26px;line-height:26px}.datetimepicker table tr td span:hover{background:#eee}.datetimepicker table tr td span.disabled,.datetimepicker table tr td span.disabled:hover{background:0;color:#999;cursor:default}.datetimepicker table tr td span.active,.datetimepicker table tr td span.active:hover,.datetimepicker table tr td span.active.disabled,.datetimepicker table tr td span.active.disabled:hover{background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-ms-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc',endColorstr='#0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.datetimepicker table tr td span.active:hover,.datetimepicker table tr td span.active:hover:hover,.datetimepicker table tr td span.active.disabled:hover,.datetimepicker table tr td span.active.disabled:hover:hover,.datetimepicker table tr td span.active:active,.datetimepicker table tr td span.active:hover:active,.datetimepicker table tr td span.active.disabled:active,.datetimepicker table tr td span.active.disabled:hover:active,.datetimepicker table tr td span.active.active,.datetimepicker table tr td span.active:hover.active,.datetimepicker table tr td span.active.disabled.active,.datetimepicker table tr td span.active.disabled:hover.active,.datetimepicker table tr td span.active.disabled,.datetimepicker table tr td span.active:hover.disabled,.datetimepicker table tr td span.active.disabled.disabled,.datetimepicker table tr td span.active.disabled:hover.disabled,.datetimepicker table tr td span.active[disabled],.datetimepicker table tr td span.active:hover[disabled],.datetimepicker table tr td span.active.disabled[disabled],.datetimepicker table tr td span.active.disabled:hover[disabled]{background-color:#04c}.datetimepicker table tr td span.active:active,.datetimepicker table tr td span.active:hover:active,.datetimepicker table tr td span.active.disabled:active,.datetimepicker table tr td span.active.disabled:hover:active,.datetimepicker table tr td span.active.active,.datetimepicker table tr td span.active:hover.active,.datetimepicker table tr td span.active.disabled.active,.datetimepicker table tr td span.active.disabled:hover.active{background-color:#039}.datetimepicker table tr td span.old{color:#999}.datetimepicker th.switch{width:145px}.datetimepicker th span.glyphicon{pointer-events:none}.datetimepicker thead tr:first-child th,.datetimepicker tfoot th{cursor:pointer}.datetimepicker thead tr:first-child th:hover,.datetimepicker tfoot th:hover{background:#eee}.input-append.date .add-on i,.input-prepend.date .add-on i,.input-group.date .input-group-addon span{cursor:pointer;width:14px;height:14px} -------------------------------------------------------------------------------- /static/assets/datetimepicker/bootstrap-datetimepicker.zh-CN.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simplified Chinese translation for bootstrap-datetimepicker 3 | * Yuan Cheung 4 | */ 5 | ;(function($){ 6 | $.fn.datetimepicker.dates['zh-CN'] = { 7 | days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], 8 | daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"], 9 | daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"], 10 | months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], 11 | monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], 12 | today: "今天", 13 | suffix: [], 14 | meridiem: ["上午", "下午"] 15 | }; 16 | }(jQuery)); 17 | -------------------------------------------------------------------------------- /static/assets/font-awesome/fonts/fontawesome-webfontba72.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/static/assets/font-awesome/fonts/fontawesome-webfontba72.eot -------------------------------------------------------------------------------- /static/assets/font-awesome/fonts/fontawesome-webfontba72.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/static/assets/font-awesome/fonts/fontawesome-webfontba72.ttf -------------------------------------------------------------------------------- /static/assets/font-awesome/fonts/fontawesome-webfontba72.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/static/assets/font-awesome/fonts/fontawesome-webfontba72.woff -------------------------------------------------------------------------------- /static/assets/font-awesome/fonts/fontawesome-webfontd41d.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/static/assets/font-awesome/fonts/fontawesome-webfontd41d.eot -------------------------------------------------------------------------------- /static/assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/static/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/static/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/static/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/assets/fonts/glyphicons-halflings-regulard41d.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaonianyr/Autotest_platform/3b5225bcab6deae233d20c8cc52350815408ff8d/static/assets/fonts/glyphicons-halflings-regulard41d.eot -------------------------------------------------------------------------------- /static/assets/js/Atp.js: -------------------------------------------------------------------------------- 1 | var host = 'http://192.168.5.142' 2 | 3 | 4 | function project(res) { 5 | if (res.code == 200) { 6 | $('#dataTables-example').dataTable().fnClearTable();//清空数据.fnClearTable();//清空数据 7 | $('#dataTables-example').dataTable().fnDestroy(); //还原初始化了的datatable 8 | var projects = res.data.projects; 9 | for (var i = 0; i < projects.length; i++) { 10 | var tr = '' 11 | tr += ''; 12 | tr += '' + (i + 1) + '' 13 | tr += '' + projects[i].name + '' 14 | tr += '' + projects[i].creatorName + '' 15 | tr += '' + projects[i].createTime + '' 16 | tr += '' + projects[i].environments.length + '' 17 | tr += '' + projects[i].remark + '' 18 | tr += '' 19 | tr += '编辑' 20 | tr += ' - ' 21 | tr += '配置' 22 | tr += ' - ' 23 | tr += '删除' 24 | tr += ''; 25 | $(".js_table").append(tr) 26 | } 27 | dataTableBuid() 28 | } else { 29 | 30 | } 31 | } 32 | 33 | function init(model, callback) { 34 | var data = JSON.stringify({"pageSize": 999999999}) 35 | $.ajax({ 36 | url: host + "/api/v1/" + model, 37 | type: 'post', 38 | dataType: 'json', 39 | data: data, 40 | success: callback 41 | }) 42 | } 43 | 44 | 45 | function dataTableBuid() { 46 | $('#dataTables-example').dataTable({ 47 | "bRetrieve": true, 48 | "bPaginate": true, //是否显示分页 49 | "bSort": true, //是否支持排序功能 50 | "bAutoWidth": false, //自动宽度 51 | "serverSide": false, 52 | "pageLength": 10, 53 | "sPaginationType": "full_numbers", //分页 54 | "oLanguage": { //多语言配置 55 | "sLengthMenu": "每页显示 10 条记录", 56 | "sZeroRecords": "对不起,查询不到任何相关数据", 57 | "sInfo": "当前显示 _START_ 到 _END_ 条,共 _TOTAL_ 条记录", 58 | "sInfoEmtpy": "找不到相关数据", 59 | "sInfoFiltered": "数据表中共为 _MAX_ 条记录)", 60 | "sProcessing": "正在加载中...", 61 | "oPaginate": { 62 | "sFirst": "第一页", 63 | "sPrevious": " 上一页 ", 64 | "sNext": " 下一页 ", 65 | "sLast": " 最后一页 " 66 | } 67 | }, 68 | }); 69 | } 70 | 71 | function create(model) { 72 | var name, remark; 73 | name = $("#projectName").val(); 74 | remark = $("#remark").val(); 75 | var data = JSON.stringify({"name": name, "remark": remark}); 76 | $.ajax({ 77 | url: host + "/api/v1/" + model + "/create", 78 | type: 'post', 79 | dataType: 'json', 80 | data: data, 81 | success: function (res) { 82 | if (res.code == 200) { 83 | $("#remark").val(""); 84 | $("#projectName").val(""); 85 | $("#newProject").modal("hide"); 86 | setTimeout(function () { 87 | init("project", project); 88 | }, 1000) 89 | } else { 90 | $("#message").html(""); 91 | $("#message").html(res.message); 92 | } 93 | } 94 | }) 95 | } 96 | 97 | function editProject(id) { 98 | $("#myModalLabel").html("编辑项目"); 99 | $("#newProject").modal("show"); 100 | $("#save").attr("onclick", "edit(" + id + ")"); 101 | $.ajax({ 102 | url: host + "/api/v1/project/" + id, 103 | type: 'post', 104 | dataType: 'json', 105 | success: function (res) { 106 | if (res.code == 200) { 107 | $("#projectName").val(res.data.name); 108 | $("#remark").val(res.data.remark); 109 | } else { 110 | $("#projectName").val(''); 111 | $("#remark").val(''); 112 | $("#message").html(""); 113 | $("#message").html(res.message); 114 | } 115 | } 116 | }) 117 | } 118 | 119 | function edit(id) { 120 | var name, remark; 121 | name = $("#projectName").val(); 122 | remark = $("#remark").val(); 123 | var data = JSON.stringify({"name": name, "remark": remark}); 124 | $.ajax({ 125 | url: host + "/api/v1/project/edit/" + id, 126 | type: 'post', 127 | dataType: 'json', 128 | data: data, 129 | success: function (res) { 130 | if (res.code == 200) { 131 | $("#newProject").modal("hide"); 132 | $("#save").attr("onclick", "create('project')"); 133 | $("#myModalLabel").html("新建项目"); 134 | $("#projectName").val(""); 135 | $("#remark").val(""); 136 | setTimeout(function () { 137 | init("project", project); 138 | }, 1000) 139 | } else { 140 | $("#message").html(""); 141 | $("#message").html(res.message); 142 | } 143 | } 144 | }) 145 | } 146 | 147 | function config(id) { 148 | // $("#projectName").val(id) 149 | } 150 | 151 | function del(model, id) { 152 | $.ajax({ 153 | url: host + "/api/v1/" + model + "/delete/" + id, 154 | type: 'post', 155 | dataType: 'json', 156 | success: function (res) { 157 | if (res.code == 200) { 158 | $("#newProject").modal("hide"); 159 | $("#save").attr("onclick", "create('project')"); 160 | $("#myModalLabel").html("新建项目"); 161 | $("#projectName").val(""); 162 | $("#remark").val(""); 163 | setTimeout(function () { 164 | init("project", project); 165 | }, 1000) 166 | } else { 167 | $("#message").html(""); 168 | $("#message").html(res.message); 169 | } 170 | } 171 | }) 172 | } 173 | -------------------------------------------------------------------------------- /static/assets/js/custom-scripts.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------ 2 | Author : www.webthemez.com 3 | License: Commons Attribution 3.0 4 | http://creativecommons.org/licenses/by/3.0/ 5 | --------------------------------------------------------- */ 6 | 7 | (function ($) { 8 | "use strict"; 9 | var mainApp = { 10 | 11 | initFunction: function () { 12 | /*MENU 13 | ------------------------------------*/ 14 | $('#main-menu').metisMenu(); 15 | 16 | $(window).bind("load resize", function () { 17 | if ($(this).width() < 768) { 18 | $('div.sidebar-collapse').addClass('collapse') 19 | } else { 20 | $('div.sidebar-collapse').removeClass('collapse') 21 | } 22 | }); 23 | 24 | /* MORRIS BAR CHART 25 | -----------------------------------------*/ 26 | Morris.Bar({ 27 | element: 'morris-bar-chart', 28 | data: [{ 29 | y: '2006', 30 | a: 100, 31 | b: 90 32 | }, { 33 | y: '2007', 34 | a: 75, 35 | b: 65 36 | }, { 37 | y: '2008', 38 | a: 50, 39 | b: 40 40 | }, { 41 | y: '2009', 42 | a: 75, 43 | b: 65 44 | }, { 45 | y: '2010', 46 | a: 50, 47 | b: 40 48 | }, { 49 | y: '2011', 50 | a: 75, 51 | b: 65 52 | }, { 53 | y: '2012', 54 | a: 100, 55 | b: 90 56 | }], 57 | xkey: 'y', 58 | ykeys: ['a', 'b'], 59 | labels: ['Series A', 'Series B'], 60 | hideHover: 'auto', 61 | resize: true 62 | }); 63 | 64 | /* MORRIS DONUT CHART 65 | ----------------------------------------*/ 66 | /* Morris.Donut({ 67 | element: 'morris-donut-chart', 68 | data: [{ 69 | label: "Download Sales", 70 | value: 12 71 | }, { 72 | label: "In-Store Sales", 73 | value: 30 74 | }, { 75 | label: "Mail-Order Sales", 76 | value: 20 77 | }], 78 | resize: true 79 | });*/ 80 | 81 | /* MORRIS AREA CHART 82 | ----------------------------------------*/ 83 | 84 | /* Morris.Area({ 85 | element: 'morris-area-chart', 86 | data: [{ 87 | period: '2010 Q1', 88 | iphone: 2666, 89 | ipad: null, 90 | itouch: 2647 91 | }, { 92 | period: '2010 Q2', 93 | iphone: 2778, 94 | ipad: 2294, 95 | itouch: 2441 96 | }, { 97 | period: '2010 Q3', 98 | iphone: 4912, 99 | ipad: 1969, 100 | itouch: 2501 101 | }, { 102 | period: '2010 Q4', 103 | iphone: 3767, 104 | ipad: 3597, 105 | itouch: 5689 106 | }, { 107 | period: '2011 Q1', 108 | iphone: 6810, 109 | ipad: 1914, 110 | itouch: 2293 111 | }, { 112 | period: '2011 Q2', 113 | iphone: 5670, 114 | ipad: 4293, 115 | itouch: 1881 116 | }, { 117 | period: '2011 Q3', 118 | iphone: 4820, 119 | ipad: 3795, 120 | itouch: 1588 121 | }, { 122 | period: '2011 Q4', 123 | iphone: 15073, 124 | ipad: 5967, 125 | itouch: 5175 126 | }, { 127 | period: '2012 Q1', 128 | iphone: 10687, 129 | ipad: 4460, 130 | itouch: 2028 131 | }, { 132 | period: '2012 Q2', 133 | iphone: 8432, 134 | ipad: 5713, 135 | itouch: 1791 136 | }], 137 | xkey: 'period', 138 | ykeys: ['iphone', 'ipad', 'itouch'], 139 | labels: ['iPhone', 'iPad', 'iPod Touch'], 140 | pointSize: 2, 141 | hideHover: 'auto', 142 | resize: true 143 | });*/ 144 | 145 | /* MORRIS LINE CHART 146 | ----------------------------------------*/ 147 | Morris.Line({ 148 | element: 'morris-line-chart', 149 | data: [{ 150 | y: '2006', 151 | a: 100, 152 | b: 90 153 | }, { 154 | y: '2007', 155 | a: 75, 156 | b: 65 157 | }, { 158 | y: '2008', 159 | a: 50, 160 | b: 40 161 | }, { 162 | y: '2009', 163 | a: 75, 164 | b: 65 165 | }, { 166 | y: '2010', 167 | a: 50, 168 | b: 40 169 | }, { 170 | y: '2011', 171 | a: 75, 172 | b: 65 173 | }, { 174 | y: '2012', 175 | a: 100, 176 | b: 90 177 | }], 178 | xkey: 'y', 179 | ykeys: ['a', 'b'], 180 | labels: ['Series A', 'Series B'], 181 | hideHover: 'auto', 182 | resize: true 183 | }); 184 | 185 | 186 | }, 187 | 188 | initialization: function () { 189 | mainApp.initFunction(); 190 | 191 | } 192 | 193 | } 194 | // Initializing /// 195 | 196 | $(document).ready(function () { 197 | mainApp.initFunction(); 198 | }); 199 | 200 | }(jQuery)); 201 | -------------------------------------------------------------------------------- /static/assets/js/custom.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------ 2 | Author : www.webthemez.com 3 | License: Commons Attribution 3.0 4 | http://creativecommons.org/licenses/by/3.0/ 5 | --------------------------------------------------------- */ 6 | 7 | (function ($) { 8 | "use strict"; 9 | var mainApp = { 10 | 11 | initFunction: function () { 12 | /*MENU 13 | ------------------------------------*/ 14 | $('#main-menu').metisMenu(); 15 | 16 | $(window).bind("load resize", function () { 17 | if ($(this).width() < 768) { 18 | $('div.sidebar-collapse').addClass('collapse') 19 | } else { 20 | $('div.sidebar-collapse').removeClass('collapse') 21 | } 22 | }); 23 | 24 | /* MORRIS BAR CHART 25 | -----------------------------------------*/ 26 | Morris.Bar({ 27 | element: 'morris-bar-chart', 28 | data: [{ 29 | y: '2006', 30 | a: 100, 31 | b: 90 32 | }, { 33 | y: '2007', 34 | a: 75, 35 | b: 65 36 | }, { 37 | y: '2008', 38 | a: 50, 39 | b: 40 40 | }, { 41 | y: '2009', 42 | a: 75, 43 | b: 65 44 | }, { 45 | y: '2010', 46 | a: 50, 47 | b: 40 48 | }, { 49 | y: '2011', 50 | a: 75, 51 | b: 65 52 | }, { 53 | y: '2012', 54 | a: 100, 55 | b: 90 56 | }], 57 | xkey: 'y', 58 | ykeys: ['a', 'b'], 59 | labels: ['Series A', 'Series B'], 60 | hideHover: 'auto', 61 | resize: true 62 | }); 63 | 64 | /* MORRIS DONUT CHART 65 | ----------------------------------------*/ 66 | Morris.Donut({ 67 | element: 'morris-donut-chart', 68 | data: [{ 69 | label: "Download Sales", 70 | value: 12 71 | }, { 72 | label: "In-Store Sales", 73 | value: 30 74 | }, { 75 | label: "Mail-Order Sales", 76 | value: 20 77 | }], 78 | resize: true 79 | }); 80 | 81 | /* MORRIS AREA CHART 82 | ----------------------------------------*/ 83 | 84 | Morris.Area({ 85 | element: 'morris-area-chart', 86 | data: [{ 87 | period: '2010 Q1', 88 | iphone: 2666, 89 | ipad: null, 90 | itouch: 2647 91 | }, { 92 | period: '2010 Q2', 93 | iphone: 2778, 94 | ipad: 2294, 95 | itouch: 2441 96 | }, { 97 | period: '2010 Q3', 98 | iphone: 4912, 99 | ipad: 1969, 100 | itouch: 2501 101 | }, { 102 | period: '2010 Q4', 103 | iphone: 3767, 104 | ipad: 3597, 105 | itouch: 5689 106 | }, { 107 | period: '2011 Q1', 108 | iphone: 6810, 109 | ipad: 1914, 110 | itouch: 2293 111 | }, { 112 | period: '2011 Q2', 113 | iphone: 5670, 114 | ipad: 4293, 115 | itouch: 1881 116 | }, { 117 | period: '2011 Q3', 118 | iphone: 4820, 119 | ipad: 3795, 120 | itouch: 1588 121 | }, { 122 | period: '2011 Q4', 123 | iphone: 15073, 124 | ipad: 5967, 125 | itouch: 5175 126 | }, { 127 | period: '2012 Q1', 128 | iphone: 10687, 129 | ipad: 4460, 130 | itouch: 2028 131 | }, { 132 | period: '2012 Q2', 133 | iphone: 8432, 134 | ipad: 5713, 135 | itouch: 1791 136 | }], 137 | xkey: 'period', 138 | ykeys: ['iphone', 'ipad', 'itouch'], 139 | labels: ['iPhone', 'iPad', 'iPod Touch'], 140 | pointSize: 2, 141 | hideHover: 'auto', 142 | resize: true 143 | }); 144 | 145 | /* MORRIS LINE CHART 146 | ----------------------------------------*/ 147 | Morris.Line({ 148 | element: 'morris-line-chart', 149 | data: [{ 150 | y: '2006', 151 | a: 100, 152 | b: 90 153 | }, { 154 | y: '2007', 155 | a: 75, 156 | b: 65 157 | }, { 158 | y: '2008', 159 | a: 50, 160 | b: 40 161 | }, { 162 | y: '2009', 163 | a: 75, 164 | b: 65 165 | }, { 166 | y: '2010', 167 | a: 50, 168 | b: 40 169 | }, { 170 | y: '2011', 171 | a: 75, 172 | b: 65 173 | }, { 174 | y: '2012', 175 | a: 100, 176 | b: 90 177 | }], 178 | xkey: 'y', 179 | ykeys: ['a', 'b'], 180 | labels: ['Series A', 'Series B'], 181 | hideHover: 'auto', 182 | resize: true 183 | }); 184 | 185 | 186 | }, 187 | 188 | initialization: function () { 189 | mainApp.initFunction(); 190 | 191 | } 192 | 193 | } 194 | // Initializing /// 195 | 196 | $(document).ready(function () { 197 | mainApp.initFunction(); 198 | }); 199 | 200 | }(jQuery)); 201 | -------------------------------------------------------------------------------- /static/assets/js/dataTables/dataTables.bootstrap.css: -------------------------------------------------------------------------------- 1 | div.dataTables_length label { 2 | float: left; 3 | text-align: left; 4 | font-weight: normal; 5 | } 6 | 7 | div.dataTables_length select { 8 | width: 75px; 9 | } 10 | 11 | div.dataTables_filter label { 12 | float: right; 13 | font-weight: normal; 14 | } 15 | 16 | div.dataTables_filter input { 17 | width: 16em; 18 | } 19 | 20 | div.dataTables_info { 21 | padding-top: 8px; 22 | } 23 | 24 | div.dataTables_paginate { 25 | float: right; 26 | margin: 0; 27 | } 28 | 29 | div.dataTables_paginate ul.pagination { 30 | margin: 2px 0; 31 | white-space: nowrap; 32 | } 33 | 34 | table.dataTable, 35 | table.dataTable td, 36 | table.dataTable th { 37 | -webkit-box-sizing: content-box; 38 | -moz-box-sizing: content-box; 39 | box-sizing: content-box; 40 | } 41 | 42 | table.dataTable { 43 | clear: both; 44 | margin-top: 6px !important; 45 | margin-bottom: 6px !important; 46 | max-width: none !important; 47 | } 48 | 49 | table.dataTable thead .sorting, 50 | table.dataTable thead .sorting_asc, 51 | table.dataTable thead .sorting_desc, 52 | table.dataTable thead .sorting_asc_disabled, 53 | table.dataTable thead .sorting_desc_disabled { 54 | cursor: pointer; 55 | } 56 | 57 | table.dataTable thead .sorting { 58 | background: url('../images/sort_both.png') no-repeat center right; 59 | } 60 | 61 | table.dataTable thead .sorting_asc { 62 | background: url('../images/sort_asc.png') no-repeat center right; 63 | } 64 | 65 | table.dataTable thead .sorting_desc { 66 | background: url('../images/sort_desc.png') no-repeat center right; 67 | } 68 | 69 | table.dataTable thead .sorting_asc_disabled { 70 | background: url('../images/sort_asc_disabled.png') no-repeat center right; 71 | } 72 | 73 | table.dataTable thead .sorting_desc_disabled { 74 | background: url('../images/sort_desc_disabled.png') no-repeat center right; 75 | } 76 | 77 | table.dataTable th:active { 78 | outline: none; 79 | } 80 | 81 | /* Scrolling */ 82 | 83 | div.dataTables_scrollHead table { 84 | margin-bottom: 0 !important; 85 | border-bottom-left-radius: 0; 86 | border-bottom-right-radius: 0; 87 | } 88 | 89 | div.dataTables_scrollHead table thead tr:last-child th:first-child, 90 | div.dataTables_scrollHead table thead tr:last-child td:first-child { 91 | border-bottom-left-radius: 0 !important; 92 | border-bottom-right-radius: 0 !important; 93 | } 94 | 95 | div.dataTables_scrollBody table { 96 | margin-top: 0 !important; 97 | margin-bottom: 0 !important; 98 | border-top: none; 99 | } 100 | 101 | div.dataTables_scrollBody tbody tr:first-child th, 102 | div.dataTables_scrollBody tbody tr:first-child td { 103 | border-top: none; 104 | } 105 | 106 | div.dataTables_scrollFoot table { 107 | margin-top: 0 !important; 108 | border-top: none; 109 | } 110 | 111 | /* 112 | * TableTools styles 113 | */ 114 | 115 | .table tbody tr.active td, 116 | .table tbody tr.active th { 117 | color: white; 118 | background-color: #08C; 119 | } 120 | 121 | .table tbody tr.active:hover td, 122 | .table tbody tr.active:hover th { 123 | background-color: #0075b0 !important; 124 | } 125 | 126 | .table tbody tr.active a { 127 | color: white; 128 | } 129 | 130 | .table-striped tbody tr.active:nth-child(odd) td, 131 | .table-striped tbody tr.active:nth-child(odd) th { 132 | background-color: #017ebc; 133 | } 134 | 135 | table.DTTT_selectable tbody tr { 136 | cursor: pointer; 137 | } 138 | 139 | div.DTTT .btn { 140 | font-size: 12px; 141 | color: #333 !important; 142 | } 143 | 144 | div.DTTT .btn:hover { 145 | text-decoration: none !important; 146 | } 147 | 148 | ul.DTTT_dropdown.dropdown-menu { 149 | z-index: 2003; 150 | } 151 | 152 | ul.DTTT_dropdown.dropdown-menu a { 153 | color: #333 !important; /* needed only when demo_page.css is included */ 154 | } 155 | 156 | ul.DTTT_dropdown.dropdown-menu li { 157 | position: relative; 158 | } 159 | 160 | ul.DTTT_dropdown.dropdown-menu li:hover a { 161 | color: white !important; 162 | background-color: #0088cc; 163 | } 164 | 165 | div.DTTT_collection_background { 166 | z-index: 2002; 167 | } 168 | 169 | /* TableTools information display */ 170 | 171 | div.DTTT_print_info.modal { 172 | height: 150px; 173 | margin-top: -75px; 174 | text-align: center; 175 | } 176 | 177 | div.DTTT_print_info h6 { 178 | margin: 1em; 179 | font-size: 28px; 180 | font-weight: normal; 181 | line-height: 28px; 182 | } 183 | 184 | div.DTTT_print_info p { 185 | font-size: 14px; 186 | line-height: 20px; 187 | } 188 | 189 | /* 190 | * FixedColumns styles 191 | */ 192 | 193 | div.DTFC_LeftHeadWrapper table, 194 | div.DTFC_LeftFootWrapper table, 195 | div.DTFC_RightHeadWrapper table, 196 | div.DTFC_RightFootWrapper table, 197 | table.DTFC_Cloned tr.even { 198 | background-color: white; 199 | } 200 | 201 | div.DTFC_RightHeadWrapper table, 202 | div.DTFC_LeftHeadWrapper table { 203 | margin-bottom: 0 !important; 204 | border-top-right-radius: 0 !important; 205 | border-bottom-left-radius: 0 !important; 206 | border-bottom-right-radius: 0 !important; 207 | } 208 | 209 | div.DTFC_RightHeadWrapper table thead tr:last-child th:first-child, 210 | div.DTFC_RightHeadWrapper table thead tr:last-child td:first-child, 211 | div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child, 212 | div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child { 213 | border-bottom-left-radius: 0 !important; 214 | border-bottom-right-radius: 0 !important; 215 | } 216 | 217 | div.DTFC_RightBodyWrapper table, 218 | div.DTFC_LeftBodyWrapper table { 219 | margin-bottom: 0 !important; 220 | border-top: none; 221 | } 222 | 223 | div.DTFC_RightBodyWrapper tbody tr:first-child th, 224 | div.DTFC_RightBodyWrapper tbody tr:first-child td, 225 | div.DTFC_LeftBodyWrapper tbody tr:first-child th, 226 | div.DTFC_LeftBodyWrapper tbody tr:first-child td { 227 | border-top: none; 228 | } 229 | 230 | div.DTFC_RightFootWrapper table, 231 | div.DTFC_LeftFootWrapper table { 232 | border-top: none; 233 | } -------------------------------------------------------------------------------- /static/assets/js/dataTables/dataTables.bootstrap.js: -------------------------------------------------------------------------------- 1 | /* Set the defaults for DataTables initialisation */ 2 | $.extend(true, $.fn.dataTable.defaults, { 3 | "sDom": "<'row'<'col-sm-6'l><'col-sm-6'f>r>" + "t" + "<'row'<'col-sm-6'i><'col-sm-6'p>>", 4 | "oLanguage": { 5 | "sLengthMenu": "_MENU_ 每页显示" 6 | } 7 | }); 8 | 9 | 10 | /* Default class modification */ 11 | $.extend($.fn.dataTableExt.oStdClasses, { 12 | "sWrapper": "dataTables_wrapper form-inline", 13 | "sFilterInput": "form-control input-sm", 14 | "sLengthSelect": "form-control input-sm" 15 | }); 16 | 17 | // In 1.10 we use the pagination renderers to draw the Bootstrap paging, 18 | // rather than custom plug-in 19 | if ($.fn.dataTable.Api) { 20 | $.fn.dataTable.defaults.renderer = 'bootstrap'; 21 | $.fn.dataTable.ext.renderer.pageButton.bootstrap = function(settings, host, idx, buttons, page, pages) { 22 | var api = new $.fn.dataTable.Api(settings); 23 | var classes = settings.oClasses; 24 | var lang = settings.oLanguage.oPaginate; 25 | var btnDisplay, btnClass; 26 | 27 | var attach = function(container, buttons) { 28 | var i, ien, node, button; 29 | var clickHandler = function(e) { 30 | e.preventDefault(); 31 | if (e.data.action !== 'ellipsis') { 32 | api.page(e.data.action).draw(false); 33 | } 34 | }; 35 | 36 | for (i = 0, ien = buttons.length; i < ien; i++) { 37 | button = buttons[i]; 38 | 39 | if ($.isArray(button)) { 40 | attach(container, button); 41 | } else { 42 | btnDisplay = ''; 43 | btnClass = ''; 44 | 45 | switch (button) { 46 | case 'ellipsis': 47 | btnDisplay = '…'; 48 | btnClass = 'disabled'; 49 | break; 50 | 51 | case 'first': 52 | btnDisplay = lang.sFirst; 53 | btnClass = button + (page > 0 ? 54 | '' : ' disabled'); 55 | break; 56 | 57 | case 'previous': 58 | btnDisplay = lang.sPrevious; 59 | btnClass = button + (page > 0 ? 60 | '' : ' disabled'); 61 | break; 62 | 63 | case 'next': 64 | btnDisplay = lang.sNext; 65 | btnClass = button + (page < pages - 1 ? 66 | '' : ' disabled'); 67 | break; 68 | 69 | case 'last': 70 | btnDisplay = lang.sLast; 71 | btnClass = button + (page < pages - 1 ? 72 | '' : ' disabled'); 73 | break; 74 | 75 | default: 76 | btnDisplay = button + 1; 77 | btnClass = page === button ? 78 | 'active' : ''; 79 | break; 80 | } 81 | 82 | if (btnDisplay) { 83 | node = $('
  • ', { 84 | 'class': classes.sPageButton + ' ' + btnClass, 85 | 'aria-controls': settings.sTableId, 86 | 'tabindex': settings.iTabIndex, 87 | 'id': idx === 0 && typeof button === 'string' ? settings.sTableId + '_' + button : null 88 | }) 89 | .append($('', { 90 | 'href': '#' 91 | }) 92 | .html(btnDisplay) 93 | ) 94 | .appendTo(container); 95 | 96 | settings.oApi._fnBindAction( 97 | node, { 98 | action: button 99 | }, clickHandler 100 | ); 101 | } 102 | } 103 | } 104 | }; 105 | 106 | attach( 107 | $(host).empty().html('
      ').children('ul'), 108 | buttons 109 | ); 110 | } 111 | } else { 112 | // Integration for 1.9- 113 | $.fn.dataTable.defaults.sPaginationType = 'bootstrap'; 114 | 115 | /* API method to get paging information */ 116 | $.fn.dataTableExt.oApi.fnPagingInfo = function(oSettings) { 117 | return { 118 | "iStart": oSettings._iDisplayStart, 119 | "iEnd": oSettings.fnDisplayEnd(), 120 | "iLength": oSettings._iDisplayLength, 121 | "iTotal": oSettings.fnRecordsTotal(), 122 | "iFilteredTotal": oSettings.fnRecordsDisplay(), 123 | "iPage": oSettings._iDisplayLength === -1 ? 0 : Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength), 124 | "iTotalPages": oSettings._iDisplayLength === -1 ? 0 : Math.ceil(oSettings.fnRecordsDisplay() / oSettings._iDisplayLength) 125 | }; 126 | }; 127 | 128 | /* Bootstrap style pagination control */ 129 | $.extend($.fn.dataTableExt.oPagination, { 130 | "bootstrap": { 131 | "fnInit": function(oSettings, nPaging, fnDraw) { 132 | var oLang = oSettings.oLanguage.oPaginate; 133 | var fnClickHandler = function(e) { 134 | e.preventDefault(); 135 | if (oSettings.oApi._fnPageChange(oSettings, e.data.action)) { 136 | fnDraw(oSettings); 137 | } 138 | }; 139 | 140 | $(nPaging).append( 141 | '' 145 | ); 146 | var els = $('a', nPaging); 147 | $(els[0]).bind('click.DT', { 148 | action: "previous" 149 | }, fnClickHandler); 150 | $(els[1]).bind('click.DT', { 151 | action: "next" 152 | }, fnClickHandler); 153 | }, 154 | 155 | "fnUpdate": function(oSettings, fnDraw) { 156 | var iListLength = 5; 157 | var oPaging = oSettings.oInstance.fnPagingInfo(); 158 | var an = oSettings.aanFeatures.p; 159 | var i, ien, j, sClass, iStart, iEnd, iHalf = Math.floor(iListLength / 2); 160 | 161 | if (oPaging.iTotalPages < iListLength) { 162 | iStart = 1; 163 | iEnd = oPaging.iTotalPages; 164 | } else if (oPaging.iPage <= iHalf) { 165 | iStart = 1; 166 | iEnd = iListLength; 167 | } else if (oPaging.iPage >= (oPaging.iTotalPages - iHalf)) { 168 | iStart = oPaging.iTotalPages - iListLength + 1; 169 | iEnd = oPaging.iTotalPages; 170 | } else { 171 | iStart = oPaging.iPage - iHalf + 1; 172 | iEnd = iStart + iListLength - 1; 173 | } 174 | 175 | for (i = 0, ien = an.length; i < ien; i++) { 176 | // Remove the middle elements 177 | $('li:gt(0)', an[i]).filter(':not(:last)').remove(); 178 | 179 | // Add the new list items and their event handlers 180 | for (j = iStart; j <= iEnd; j++) { 181 | sClass = (j == oPaging.iPage + 1) ? 'class="active"' : ''; 182 | $('
    • ' + j + '
    • ') 183 | .insertBefore($('li:last', an[i])[0]) 184 | .bind('click', function(e) { 185 | e.preventDefault(); 186 | oSettings._iDisplayStart = (parseInt($('a', this).text(), 10) - 1) * oPaging.iLength; 187 | fnDraw(oSettings); 188 | }); 189 | } 190 | 191 | // Add / remove disabled classes from the static elements 192 | if (oPaging.iPage === 0) { 193 | $('li:first', an[i]).addClass('disabled'); 194 | } else { 195 | $('li:first', an[i]).removeClass('disabled'); 196 | } 197 | 198 | if (oPaging.iPage === oPaging.iTotalPages - 1 || oPaging.iTotalPages === 0) { 199 | $('li:last', an[i]).addClass('disabled'); 200 | } else { 201 | $('li:last', an[i]).removeClass('disabled'); 202 | } 203 | } 204 | } 205 | } 206 | }); 207 | } 208 | 209 | 210 | /* 211 | * TableTools Bootstrap compatibility 212 | * Required TableTools 2.1+ 213 | */ 214 | if ($.fn.DataTable.TableTools) { 215 | // Set the classes that TableTools uses to something suitable for Bootstrap 216 | $.extend(true, $.fn.DataTable.TableTools.classes, { 217 | "container": "DTTT btn-group", 218 | "buttons": { 219 | "normal": "btn btn-default", 220 | "disabled": "disabled" 221 | }, 222 | "collection": { 223 | "container": "DTTT_dropdown dropdown-menu", 224 | "buttons": { 225 | "normal": "", 226 | "disabled": "disabled" 227 | } 228 | }, 229 | "print": { 230 | "info": "DTTT_print_info modal" 231 | }, 232 | "select": { 233 | "row": "active" 234 | } 235 | }); 236 | 237 | // Have the collection use a bootstrap compatible dropdown 238 | $.extend(true, $.fn.DataTable.TableTools.DEFAULTS.oTags, { 239 | "collection": { 240 | "container": "ul", 241 | "button": "li", 242 | "liner": "a" 243 | } 244 | }); 245 | } 246 | -------------------------------------------------------------------------------- /static/assets/js/jquery.metisMenu.js: -------------------------------------------------------------------------------- 1 | ;(function ($, window, document, undefined) { 2 | 3 | var pluginName = "metisMenu", 4 | defaults = { 5 | toggle: true 6 | }; 7 | 8 | function Plugin(element, options) { 9 | this.element = element; 10 | this.settings = $.extend({}, defaults, options); 11 | this._defaults = defaults; 12 | this._name = pluginName; 13 | this.init(); 14 | } 15 | 16 | Plugin.prototype = { 17 | init: function () { 18 | 19 | var $this = $(this.element), 20 | $toggle = this.settings.toggle; 21 | 22 | $this.find('li.active').has('ul').children('ul').addClass('collapse in'); 23 | $this.find('li').not('.active').has('ul').children('ul').addClass('collapse'); 24 | 25 | $this.find('li').has('ul').children('a').on('click', function (e) { 26 | e.preventDefault(); 27 | 28 | $(this).parent('li').toggleClass('active').children('ul').collapse('toggle'); 29 | 30 | if ($toggle) { 31 | $(this).parent('li').siblings().removeClass('active').children('ul.in').collapse('hide'); 32 | } 33 | }); 34 | } 35 | }; 36 | 37 | $.fn[ pluginName ] = function (options) { 38 | return this.each(function () { 39 | if (!$.data(this, "plugin_" + pluginName)) { 40 | $.data(this, "plugin_" + pluginName, new Plugin(this, options)); 41 | } 42 | }); 43 | }; 44 | 45 | })(jQuery, window, document); 46 | -------------------------------------------------------------------------------- /static/assets/js/morris/morris-0.4.3.min.css: -------------------------------------------------------------------------------- 1 | .morris-hover{position:absolute;z-index:1000;}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);font-family:sans-serif;font-size:12px;text-align:center;}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0;} 2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0;} -------------------------------------------------------------------------------- /static/assets/js/web-report-default.min.js: -------------------------------------------------------------------------------- 1 | "use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj};function randomString(len){len=len||10;for(var $chars="ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz123456789",maxPos=$chars.length,pwd="",i=0;i1*datatime)&&(markUv=randomString(),localStorage.setItem("ps_markUv",markUv),localStorage.setItem("ps_markUvTime",new Date(today).getTime())),markUv}(),type:type,url:location.href};!function(){var reslist=conf.resourceList,filterUrl=opt.filterUrl,newlist=[];if(reslist&&reslist.length&&filterUrl&&filterUrl.length)for(var i=0;i 2 | 3 | {% load static %} 4 | 5 | 6 | 7 | 查看结果 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
      17 | 18 | 37 | 38 | 39 | 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 | 115 |
      116 | 117 |
      118 | url 119 |
      120 |
      121 |
      122 | 123 |
      124 | url 125 |
      126 |
      127 | 128 |
      129 | 130 |
      131 |
      132 |
      133 | 134 |
      135 | 136 |
      137 |
      138 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 |
      序号浏览器测试环境测试数据预期结果实际结果备注信息测试开始时间测试结束时间
      155 |
      156 |
      157 |
      158 |
      159 | 160 |
      161 | 返回 163 |
      164 |
      165 | 166 |
      167 |
      168 |
      169 |
      170 |
      171 | 172 | 173 | 174 |
      175 |

      176 |
      177 |
      178 | 179 |
      180 | 181 |
      182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /templates/page/首页.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | {% load static %} 4 | 5 | 6 | 7 | POM - 自动化测试平台 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
      17 | 18 | 36 | 37 | 38 | 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 | 115 | 116 | 117 | 118 | 119 | 120 | 121 |
      项目名称用例数量总成功数总失败数今天成功数今天失败数
      122 |
      123 | 124 |
      125 |
      126 | 127 |
      128 |
      129 | 130 | 131 |
      132 |

      133 |
      134 |
      135 | 136 |
      137 | 138 |
      139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 279 | 280 | --------------------------------------------------------------------------------