├── InterfaceAutoTest ├── db.sqlite3 ├── interfacetestplatform │ ├── utils │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-36.pyc │ │ │ ├── data_process.cpython-36.pyc │ │ │ └── request_process.cpython-36.pyc │ │ ├── request_process.py │ │ └── data_process.py │ ├── migrations │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-36.pyc │ │ │ ├── 0002_module.cpython-36.pyc │ │ │ ├── 0001_initial.cpython-36.pyc │ │ │ ├── 0003_testcase.cpython-36.pyc │ │ │ ├── 0004_casesuite.cpython-36.pyc │ │ │ ├── 0006_interfaceserver.cpython-36.pyc │ │ │ ├── 0005_auto_20210714_1659.cpython-36.pyc │ │ │ ├── 0007_testcaseexecuteresult.cpython-36.pyc │ │ │ ├── 0008_testcaseexecuteresult_execute_var.cpython-36.pyc │ │ │ ├── 0010_alter_testcaseexecuteresult_exception_info.cpython-36.pyc │ │ │ ├── 0009_rename_execute_var_testcaseexecuteresult_extract_var.cpython-36.pyc │ │ │ └── 0011_casesuiteexecuterecord_casesuitetestcaseexecuterecord.cpython-36.pyc │ │ ├── 0009_rename_execute_var_testcaseexecuteresult_extract_var.py │ │ ├── 0008_testcaseexecuteresult_execute_var.py │ │ ├── 0010_alter_testcaseexecuteresult_exception_info.py │ │ ├── 0004_casesuite.py │ │ ├── 0006_interfaceserver.py │ │ ├── 0001_initial.py │ │ ├── 0002_module.py │ │ ├── 0005_auto_20210714_1659.py │ │ ├── 0007_testcaseexecuteresult.py │ │ ├── 0003_testcase.py │ │ └── 0011_casesuiteexecuterecord_casesuitetestcaseexecuterecord.py │ ├── tests.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── apps.cpython-36.pyc │ │ ├── form.cpython-36.pyc │ │ ├── task.cpython-36.pyc │ │ ├── urls.cpython-36.pyc │ │ ├── admin.cpython-36.pyc │ │ ├── celery.cpython-36.pyc │ │ ├── models.cpython-36.pyc │ │ ├── views.cpython-36.pyc │ │ └── __init__.cpython-36.pyc │ ├── apps.py │ ├── templates │ │ ├── index.html │ │ ├── show_exception.html │ │ ├── case_result_diff.html │ │ ├── login.html │ │ ├── test_case_detail.html │ │ ├── project_statistics.html │ │ ├── module_statistics.html │ │ ├── project.html │ │ ├── module.html │ │ ├── case_suite_execute_record.html │ │ ├── test_case_execute_records.html │ │ ├── base.html │ │ ├── suite_case_execute_record.html │ │ ├── add_case_in_suite.html │ │ ├── test_case.html │ │ ├── case_suite.html │ │ ├── case_suite_statistics.html │ │ ├── show_and_delete_case_in_suite.html │ │ └── suite_case_statistics.html │ ├── form.py │ ├── celery.py │ ├── admin.py │ ├── urls.py │ ├── models.py │ ├── task.py │ └── views.py ├── InterfaceAutoTest │ ├── __pycache__ │ │ ├── urls.cpython-36.pyc │ │ ├── wsgi.cpython-36.pyc │ │ ├── __init__.cpython-36.pyc │ │ └── settings.cpython-36.pyc │ ├── __init__.py │ ├── asgi.py │ ├── wsgi.py │ ├── urls.py │ └── settings.py ├── static │ ├── bootstrap │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── js │ │ │ └── npm.js │ │ └── css │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap-theme.min.css.map │ │ │ └── bootstrap-theme.css │ └── css │ │ ├── login.css │ │ ├── dialog.css │ │ └── dashboard.css ├── .idea │ ├── modules.xml │ ├── misc.xml │ ├── InterfaceAutoTest.iml │ └── inspectionProfiles │ │ └── Project_Default.xml └── manage.py ├── .gitattributes └── README.md /InterfaceAutoTest/db.sqlite3: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InterfaceAutoTestPlatform 2 | Django接口自动化测试平台 3 | 4 | 博客详解:https://www.cnblogs.com/juno3550/p/15005974.html 5 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | from .celery import app as celery_app 3 | 4 | __all__ = ['celery_app'] 5 | -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/InterfaceAutoTest/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/__pycache__/wsgi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/InterfaceAutoTest/__pycache__/wsgi.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/InterfaceAutoTest/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/__pycache__/settings.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/InterfaceAutoTest/__pycache__/settings.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/apps.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/apps.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/form.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/form.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/task.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/task.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/celery.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/celery.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/static/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/static/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /InterfaceAutoTest/static/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/static/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /InterfaceAutoTest/static/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/static/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/static/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/__init__.py: -------------------------------------------------------------------------------- 1 | import pymysql 2 | 3 | pymysql.version_info = (1, 4, 13, "final", 0) # 指定版本。在出现“mysqlclient 1.4.0 or newer is required; you have 0.9.3.”报错时加上此行 4 | pymysql.install_as_MySQLdb() -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/utils/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/utils/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/utils/__pycache__/data_process.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/utils/__pycache__/data_process.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class InterfacetestplatformConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'interfacetestplatform' 7 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0002_module.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0002_module.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/utils/__pycache__/request_process.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/utils/__pycache__/request_process.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0003_testcase.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0003_testcase.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0004_casesuite.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0004_casesuite.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0006_interfaceserver.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0006_interfaceserver.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0005_auto_20210714_1659.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0005_auto_20210714_1659.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0007_testcaseexecuteresult.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0007_testcaseexecuteresult.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %}主页{% endblock %} 3 | {% block content %} 4 | {% if request.user.is_authenticated %} 5 |

你好,{{ request.user.username }}!欢迎回来!

6 | {% endif %} 7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0008_testcaseexecuteresult_execute_var.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0008_testcaseexecuteresult_execute_var.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0010_alter_testcaseexecuteresult_exception_info.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0010_alter_testcaseexecuteresult_exception_info.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0009_rename_execute_var_testcaseexecuteresult_extract_var.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0009_rename_execute_var_testcaseexecuteresult_extract_var.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0011_casesuiteexecuterecord_casesuitetestcaseexecuterecord.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juno3550/InterfaceAutoTestPlatform/HEAD/InterfaceAutoTest/interfacetestplatform/migrations/__pycache__/0011_casesuiteexecuterecord_casesuitetestcaseexecuterecord.cpython-36.pyc -------------------------------------------------------------------------------- /InterfaceAutoTest/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/show_exception.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}异常信息{% endblock %} 4 | {% block content %} 5 | 6 |

异常信息如下:

7 |

{{ exception_info|default_if_none:"" }}

8 | 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/form.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | 4 | class UserForm(forms.Form): 5 | username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'})) 6 | password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'})) 7 | 8 | -------------------------------------------------------------------------------- /InterfaceAutoTest/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for InterfaceAutoTest project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'InterfaceAutoTest.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for InterfaceAutoTest 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/3.2/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', 'InterfaceAutoTest.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /InterfaceAutoTest/static/bootstrap/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /InterfaceAutoTest/.idea/InterfaceAutoTest.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0009_rename_execute_var_testcaseexecuteresult_extract_var.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-07-17 17:56 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('interfacetestplatform', '0008_testcaseexecuteresult_execute_var'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='testcaseexecuteresult', 15 | old_name='execute_var', 16 | new_name='extract_var', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0008_testcaseexecuteresult_execute_var.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-07-17 17:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('interfacetestplatform', '0007_testcaseexecuteresult'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='testcaseexecuteresult', 15 | name='execute_var', 16 | field=models.CharField(max_length=1024, null=True, verbose_name='关联参数'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0010_alter_testcaseexecuteresult_exception_info.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-07-17 22:03 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('interfacetestplatform', '0009_rename_execute_var_testcaseexecuteresult_extract_var'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='testcaseexecuteresult', 15 | name='exception_info', 16 | field=models.CharField(blank=True, max_length=2048, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /InterfaceAutoTest/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/celery.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | import os 3 | from celery import Celery 4 | from django.conf import settings 5 | 6 | 7 | # 参数为项目名称 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'InterfaceAutoTest.settings') # 设置django环境 9 | # 参数为项目名称 10 | app = Celery('InterfaceAutoTest', backend='redis://127.0.0.1:6379/1', broker='redis://127.0.0.1:6379/2') 11 | 12 | app.config_from_object('django.conf:settings') # 使用CELERY_作为前缀,在settings中写配置 13 | 14 | app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) # 发现任务文件每个app下的task.py 15 | 16 | # 时区 17 | app.conf.timezone = 'Asia/Shanghai' 18 | # 是否使用UTC 19 | app.conf.enable_utc = False 20 | -------------------------------------------------------------------------------- /InterfaceAutoTest/static/css/login.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #eee; 3 | } 4 | .form-login { 5 | max-width: 330px; 6 | padding: 15px; 7 | margin: 0 auto; 8 | } 9 | .form-login .form-control { 10 | position: relative; 11 | height: auto; 12 | -webkit-box-sizing: border-box; 13 | -moz-box-sizing: border-box; 14 | box-sizing: border-box; 15 | padding: 10px; 16 | font-size: 16px; 17 | } 18 | .form-login .form-control:focus { 19 | z-index: 2; 20 | } 21 | .form-login input[type="text"] { 22 | margin-bottom: -1px; 23 | border-bottom-right-radius: 0; 24 | border-bottom-left-radius: 0; 25 | } 26 | .form-login input[type="password"] { 27 | margin-bottom: 10px; 28 | border-top-left-radius: 0; 29 | border-top-right-radius: 0; 30 | } 31 | -------------------------------------------------------------------------------- /InterfaceAutoTest/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'InterfaceAutoTest.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/urls.py: -------------------------------------------------------------------------------- 1 | """InterfaceAutoTest URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/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.urls import path, include 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | path('', include('interfacetestplatform.urls')), 22 | ] 23 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/case_result_diff.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}结果对比差异{% endblock %} 4 | 5 | {% block content %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 23 | 24 | 25 |
上次执行结果本次执行结果
16 |
17 |
{{ last_time_execute_response | safe }}
18 |
19 |
21 |
{{ present_response | safe }}
22 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /InterfaceAutoTest/static/css/dialog.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | width: 200px; 3 | height: 50px; 4 | font-size: 20px; 5 | color: white; 6 | background-color: #006DCC; 7 | border: 0px; 8 | border-radius: 10px 9 | } 10 | .btn:hover { 11 | box-shadow: 0 0 5px 5px darkgray; 12 | } 13 | 14 | .dialog { 15 | display: none; 16 | position: fixed; 17 | top: 0; 18 | left: 0; 19 | width: 100%; 20 | height: 100%; 21 | overflow: auto; 22 | background-color: rgba(0, 0, 0, 0.4); 23 | } 24 | 25 | .content { 26 | width: 500px; 27 | height: 300px; 28 | margin: 100px auto; 29 | background-color: #fefefe; 30 | border-radius: 10px; 31 | box-shadow: 0 0 5px 5px darkgray; 32 | } 33 | .aclose{ 34 | width: 500px; 35 | height: 60px; 36 | text-align: center; 37 | } 38 | .aclose span{ 39 | line-height: 70px; 40 | font-size: 26px; 41 | font-weight: 700; 42 | } 43 | .contain{ 44 | width: 500px; 45 | height: 230px; 46 | font-size: 20px; 47 | margin-top: 10px; 48 | text-align: center; 49 | } 50 | 51 | .close { 52 | color: #aaa; 53 | float: right; 54 | margin-right: 15px; 55 | font-size: 40px; 56 | font-weight: bold; 57 | text-decoration: none; 58 | } -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0004_casesuite.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-14 16:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('interfacetestplatform', '0003_testcase'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='CaseSuite', 15 | fields=[ 16 | ('id', models.AutoField(primary_key=True, serialize=False)), 17 | ('Suite_desc', models.CharField(blank=True, max_length=100, null=True, verbose_name='用例集合描述')), 18 | ('if_execute', models.IntegerField(default=0, help_text='0:执行;1:不执行', verbose_name='是否执行')), 19 | ('test_case_model', models.CharField(blank=True, help_text='data/keyword', max_length=100, null=True, verbose_name='测试执行模式')), 20 | ('creator', models.CharField(blank=True, max_length=50, null=True)), 21 | ('create_time', models.DateTimeField(auto_now=True, verbose_name='创建时间')), 22 | ], 23 | options={ 24 | 'verbose_name': '用例集合', 25 | 'verbose_name_plural': '用例集合', 26 | }, 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}登录{% endblock %} 4 | {% block css %} 5 | 6 | {% endblock %} 7 | 8 | {% block content %} 9 |
10 |
11 | 33 |
34 |
35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .import models 3 | 4 | 5 | class ProjectAdmin(admin.ModelAdmin): 6 | list_display = ("id", "name", "proj_owner", "test_owner", "dev_owner", "desc", "create_time", "update_time") 7 | 8 | admin.site.register(models.Project, ProjectAdmin) 9 | 10 | 11 | class ModuleAdmin(admin.ModelAdmin): 12 | list_display = ("id", "name", "belong_project", "test_owner", "desc", "create_time", "update_time") 13 | 14 | admin.site.register(models.Module, ModuleAdmin) 15 | 16 | 17 | class TestCaseAdmin(admin.ModelAdmin): 18 | list_display = ( 19 | "id", "case_name", "belong_project", "belong_module", "request_data", "uri", "assert_key", "maintainer", 20 | "extract_var", "request_method", "status", "created_time", "updated_time", "user") 21 | 22 | admin.site.register(models.TestCase, TestCaseAdmin) 23 | 24 | 25 | class CaseSuiteAdmin(admin.ModelAdmin): 26 | list_display = ("id", "suite_desc", "creator", "create_time") 27 | 28 | admin.site.register(models.CaseSuite, CaseSuiteAdmin) 29 | 30 | 31 | class InterfaceServerAdmin(admin.ModelAdmin): 32 | list_display = ("id", "env", "ip", "port", "remark", "create_time") 33 | 34 | admin.site.register(models.InterfaceServer, InterfaceServerAdmin) 35 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0006_interfaceserver.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-15 16:21 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('interfacetestplatform', '0005_auto_20210714_1659'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='InterfaceServer', 15 | fields=[ 16 | ('id', models.AutoField(primary_key=True, serialize=False)), 17 | ('env', models.CharField(default='', max_length=50, verbose_name='环境')), 18 | ('ip', models.CharField(default='', max_length=50, verbose_name='ip')), 19 | ('port', models.CharField(default='', max_length=100, verbose_name='端口')), 20 | ('remark', models.CharField(max_length=100, null=True, verbose_name='备注')), 21 | ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 22 | ('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间')), 23 | ], 24 | options={ 25 | 'verbose_name': '接口地址配置表', 26 | 'verbose_name_plural': '接口地址配置表', 27 | }, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-13 08:40 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Project', 16 | fields=[ 17 | ('id', models.AutoField(primary_key=True, serialize=False)), 18 | ('name', models.CharField(max_length=50, unique=True, verbose_name='项目名称')), 19 | ('proj_owner', models.CharField(max_length=20, verbose_name='项目负责人')), 20 | ('test_owner', models.CharField(max_length=20, verbose_name='测试负责人')), 21 | ('dev_owner', models.CharField(max_length=20, verbose_name='开发负责人')), 22 | ('desc', models.CharField(max_length=100, null=True, verbose_name='项目描述')), 23 | ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='项目创建时间')), 24 | ('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='项目更新时间')), 25 | ], 26 | options={ 27 | 'verbose_name': '项目信息表', 28 | 'verbose_name_plural': '项目信息表', 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0002_module.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-14 09:55 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('interfacetestplatform', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Module', 16 | fields=[ 17 | ('id', models.AutoField(primary_key=True, serialize=False)), 18 | ('name', models.CharField(max_length=50, verbose_name='模块名称')), 19 | ('test_owner', models.CharField(max_length=50, verbose_name='测试负责人')), 20 | ('desc', models.CharField(max_length=100, null=True, verbose_name='简要描述')), 21 | ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 22 | ('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间')), 23 | ('belong_project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.project')), 24 | ], 25 | options={ 26 | 'verbose_name': '模块信息表', 27 | 'verbose_name_plural': '模块信息表', 28 | }, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0005_auto_20210714_1659.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-14 16:59 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('interfacetestplatform', '0004_casesuite'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='casesuite', 16 | options={'verbose_name': '用例集合表', 'verbose_name_plural': '用例集合表'}, 17 | ), 18 | migrations.RenameField( 19 | model_name='casesuite', 20 | old_name='Suite_desc', 21 | new_name='suite_desc', 22 | ), 23 | migrations.CreateModel( 24 | name='SuiteCase', 25 | fields=[ 26 | ('id', models.AutoField(primary_key=True, serialize=False)), 27 | ('status', models.IntegerField(default=1, help_text='0:有效,1:无效', verbose_name='是否有效')), 28 | ('create_time', models.DateTimeField(auto_now=True, verbose_name='创建时间')), 29 | ('case_suite', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.casesuite', verbose_name='用例集合')), 30 | ('test_case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.testcase', verbose_name='测试用例')), 31 | ], 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/test_case_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}用例详情{% endblock %} 4 | 5 | {% block content %} 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
id接口名称所属项目所属模块接口地址请求数据断言内容编写人员提取变量表达式维护人创建人创建时间更新时间
{{ test_case.id }}{{ test_case.case_name }}{{ test_case.belong_project }}{{ test_case.belong_module }}{{ test_case.uri }}{{ test_case.request_data }}{{ test_case.assert_key }}{{ test_case.maintainer }}{{ test_case.extract_var }}{{ test_case.maintainer }}{{ test_case.user.username }}{{ test_case.created_time|date:"Y-n-d H:i" }}{{ test_case.updated_time|date:"Y-n-d H:i" }}
43 |
44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/project_statistics.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}模块测试结果统计{% endblock %} 4 | {% block content %} 5 | 6 | 14 | 15 |

16 | 【{{ test_project.name }}】执行统计结果:成功 {{ success_num }} 次,失败 {{ fail_num }} 次 17 |

18 |

19 | 20 |

21 |
22 | 23 | 24 | 59 | 60 | 61 | {% endblock %} 62 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/module_statistics.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}模块测试结果统计{% endblock %} 4 | {% block content %} 5 | 6 | 15 | 16 |

17 | 【{{ test_module.name }}】执行统计结果:成功 {{ success_num }} 次,失败 {{ fail_num }} 次 18 |

19 |

20 | 21 |

22 |
23 | 24 | 25 | 60 | 61 | 62 | {% endblock %} 63 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/project.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}主页{% endblock %} 4 | 5 | {% block content %} 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% for project in projects %} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {% endfor %} 36 | 37 |
id项目名称项目负责人测试负责人开发负责人简要描述创建时间更新时间测试结果统计
{{ project.id }}{{ project.name }}{{ project.proj_owner }}{{ project.test_owner }}{{ project.dev_owner }}{{ project.desc }}{{ project.create_time|date:"Y-n-d H:i" }}{{ project.update_time|date:"Y-n-d H:i" }} 查看
38 |
39 | 40 | {# 实现分页标签的代码 #} 41 | {# 这里使用 bootstrap 渲染页面 #} 42 |
43 | 59 |
60 | {% endblock %} 61 | -------------------------------------------------------------------------------- /InterfaceAutoTest/static/css/dashboard.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Base structure 3 | */ 4 | 5 | /* Move down content because we have a fixed navbar that is 50px tall */ 6 | body { 7 | padding-top: 20px; 8 | } 9 | 10 | 11 | /* 12 | * Global add-ons 13 | */ 14 | 15 | .sub-header { 16 | padding-bottom: 10px; 17 | border-bottom: 1px solid #eee; 18 | } 19 | 20 | /* 21 | * Top navigation 22 | * Hide default border to remove 1px line. 23 | */ 24 | .navbar-fixed-top { 25 | border: 0; 26 | } 27 | 28 | /* 29 | * Sidebar 30 | */ 31 | 32 | /* Hide for mobile, show later */ 33 | .sidebar { 34 | display: none; 35 | } 36 | @media (min-width: 768px) { 37 | .sidebar { 38 | position: fixed; 39 | top: 51px; 40 | bottom: 0; 41 | left: 0; 42 | z-index: 1000; 43 | display: block; 44 | padding: 20px; 45 | overflow-x: hidden; 46 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ 47 | background-color: #f5f5f5; 48 | border-right: 1px solid #eee; 49 | } 50 | } 51 | 52 | /* Sidebar navigation */ 53 | .nav-sidebar { 54 | margin-right: -21px; /* 20px padding + 1px border */ 55 | margin-bottom: 20px; 56 | margin-left: -20px; 57 | } 58 | .nav-sidebar > li > a { 59 | padding-right: 20px; 60 | padding-left: 20px; 61 | } 62 | .nav-sidebar > .active > a, 63 | .nav-sidebar > .active > a:hover, 64 | .nav-sidebar > .active > a:focus { 65 | color: #fff; 66 | background-color: #428bca; 67 | } 68 | 69 | 70 | /* 71 | * Main content 72 | */ 73 | 74 | .main { 75 | padding: 20px; 76 | } 77 | @media (min-width: 768px) { 78 | .main { 79 | padding-right: 40px; 80 | padding-left: 40px; 81 | } 82 | } 83 | .main .page-header { 84 | margin-top: 0; 85 | } 86 | 87 | 88 | /* 89 | * Placeholder dashboard ideas 90 | */ 91 | 92 | .placeholders { 93 | margin-bottom: 30px; 94 | text-align: center; 95 | } 96 | .placeholders h4 { 97 | margin-bottom: 0; 98 | } 99 | .placeholder { 100 | margin-bottom: 20px; 101 | } 102 | .placeholder img { 103 | display: inline-block; 104 | border-radius: 50%; 105 | } 106 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0007_testcaseexecuteresult.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-07-17 16:52 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | import smart_selects.db_fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('interfacetestplatform', '0006_interfaceserver'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='TestCaseExecuteResult', 17 | fields=[ 18 | ('id', models.AutoField(primary_key=True, serialize=False)), 19 | ('status', models.IntegerField(help_text='0:表示未执行,1:表示已执行', null=True)), 20 | ('exception_info', models.CharField(blank=True, max_length=500, null=True)), 21 | ('request_data', models.CharField(max_length=1024, null=True, verbose_name='请求体')), 22 | ('response_data', models.CharField(max_length=1024, null=True, verbose_name='响应字符串')), 23 | ('execute_result', models.CharField(max_length=1024, null=True, verbose_name='执行结果')), 24 | ('last_time_response_data', models.CharField(max_length=1024, null=True, verbose_name='上一次响应字符串')), 25 | ('execute_total_time', models.CharField(max_length=1024, null=True, verbose_name='执行耗时')), 26 | ('execute_start_time', models.CharField(blank=True, max_length=300, null=True, verbose_name='执行开始时间')), 27 | ('execute_end_time', models.CharField(blank=True, max_length=300, null=True, verbose_name='执行结束时间')), 28 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 29 | ('updated_time', models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间')), 30 | ('belong_test_case', smart_selects.db_fields.GroupedForeignKey(group_field='belong_test_case', on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.testcase', verbose_name='所属用例')), 31 | ], 32 | options={ 33 | 'verbose_name': '用例执行结果记录表', 34 | 'verbose_name_plural': '用例执行结果记录表', 35 | }, 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | from . import views 3 | 4 | 5 | urlpatterns = [ 6 | path('', views.index), 7 | path('login/', views.login), 8 | path('logout/', views.logout), 9 | path('project/', views.project, name='project'), 10 | path('module/', views.module, name='module'), 11 | path('test_case/', views.test_case, name="test_case"), 12 | re_path('test_case_detail/(?P[0-9]+)', views.test_case_detail, name="test_case_detail"), 13 | re_path('module_test_cases/(?P[0-9]+)/$', views.module_test_cases, name="module_test_cases"), 14 | path('case_suite/', views.case_suite, name="case_suite"), 15 | re_path('add_case_in_suite/(?P[0-9]+)', views.add_case_in_suite, name="add_case_in_suite"), 16 | re_path('show_and_delete_case_in_suite/(?P[0-9]+)', views.show_and_delete_case_in_suite, name="show_and_delete_case_in_suite"), 17 | path('test_case_execute_record/', views.test_case_execute_record, name="test_case_execute_record"), 18 | re_path('case_result_diff/(?P[0-9]+)', views.case_result_diff, name="case_result_diff"), 19 | re_path('show_exception/(?P[0-9]+)$', views.show_exception, name="show_exception"), 20 | path('case_suite_execute_record/', views.case_suite_execute_record, name="case_suite_execute_record"), 21 | re_path('suite_case_execute_record/(?P[0-9]+)', views.suite_case_execute_record, name="suite_case_execute_record"), 22 | re_path('suite_case_result_diff/(?P[0-9]+)', views.suite_case_result_diff, name="suite_case_result_diff"), 23 | re_path('suite_case_exception/(?P[0-9]+)', views.suite_case_exception, name="suite_case_exception"), 24 | re_path('suite_case_statistics/(?P[0-9]+)', views.suite_case_statistics, name="suite_case_statistics"), 25 | re_path('case_suite_statistics/(?P[0-9]+)', views.case_suite_statistics, name="case_suite_statistics"), 26 | re_path('module_statistics/(?P[0-9]+)', views.module_statistics, name="module_statistics"), 27 | re_path('project_statistics/(?P[0-9]+)', views.project_statistics, name="project_statistics"), 28 | ] -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/module.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}模块{% endblock %} 4 | 5 | {% block content %} 6 |
7 | {% csrf_token %} 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% for module in modules %} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {% endfor %} 41 | 42 | 43 |
id模块名称所属项目测试负责人模块描述创建时间更新时间测试结果统计
{{ module.id }}{{ module.name }}{{ module.belong_project.name }}{{ module.test_owner }}{{ module.desc }}{{ module.create_time|date:"Y-n-d H:i" }}{{ module.update_time|date:"Y-n-d H:i" }}查看
44 |
45 | 46 | {# 实现分页标签的代码 #} 47 | {# 这里使用 bootstrap 渲染页面 #} 48 |
49 | 65 |
66 | {% endblock %} 67 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/utils/request_process.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | # from Util.Log import logger 4 | 5 | 6 | # 此函数封装了get请求、post和put请求的方法 7 | def request_process(url, request_method, request_content): 8 | print("-------- 开始调用接口 --------") 9 | if request_method == "get": 10 | try: 11 | if isinstance(request_content, dict): 12 | print("接口地址:%s" % url) 13 | print("请求数据:%s" % request_content) 14 | r = requests.get(url, params=json.dumps(request_content)) 15 | else: 16 | r = requests.get(url+str(request_content)) 17 | print("接口地址:%s" % r.url) 18 | print("请求数据:%s" % request_content) 19 | 20 | except Exception as e: 21 | print("get方法请求发生异常:请求的url是%s, 请求的内容是%s\n发生的异常信息如下:%s" % (url, request_content, e)) 22 | r = None 23 | return r 24 | elif request_method == "post": 25 | try: 26 | if isinstance(request_content, dict): 27 | print("接口地址:%s" % url) 28 | print("请求数据:%s" % json.dumps(request_content)) 29 | r = requests.post(url, data=json.dumps(request_content)) 30 | else: 31 | raise ValueError 32 | except ValueError as e: 33 | print("post方法请求发生异常:请求的url是%s, 请求的内容是%s\n发生的异常信息如下:%s" % (url, request_content, "请求参数不是字典类型")) 34 | r = None 35 | except Exception as e: 36 | print("post方法请求发生异常:请求的url是%s, 请求的内容是%s\n发生的异常信息如下:%s" % (url, request_content, e)) 37 | r = None 38 | return r 39 | elif request_method == "put": 40 | try: 41 | if isinstance(request_content, dict): 42 | print("接口地址:%s" % url) 43 | print("请求数据:%s" % json.dumps(request_content)) 44 | r = requests.put(url, data=json.dumps(request_content)) 45 | else: 46 | raise ValueError 47 | except ValueError as e: 48 | print("put方法请求发生异常:请求的url是%s, 请求的内容是%s\n发生的异常信息如下:%s" % (url, request_content, "请求参数不是字典类型")) 49 | r = None 50 | except Exception as e: 51 | print("put方法请求发生异常:请求的url是%s, 请求的内容是%s\n发生的异常信息如下:%s" % (url, request_content, e)) 52 | r = None 53 | return r 54 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0003_testcase.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2021-07-14 11:07 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import smart_selects.db_fields 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('interfacetestplatform', '0002_module'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='TestCase', 19 | fields=[ 20 | ('id', models.AutoField(primary_key=True, serialize=False)), 21 | ('case_name', models.CharField(max_length=50, verbose_name='用例名称')), 22 | ('request_data', models.CharField(default='', max_length=1024, verbose_name='请求数据')), 23 | ('uri', models.CharField(default='', max_length=1024, verbose_name='接口地址')), 24 | ('assert_key', models.CharField(max_length=1024, null=True, verbose_name='断言内容')), 25 | ('maintainer', models.CharField(default='', max_length=1024, verbose_name='编写人员')), 26 | ('extract_var', models.CharField(max_length=1024, null=True, verbose_name='提取变量表达式')), 27 | ('request_method', models.CharField(max_length=1024, null=True, verbose_name='请求方式')), 28 | ('status', models.IntegerField(help_text='0:表示有效,1:表示无效,用于软删除', null=True)), 29 | ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 30 | ('updated_time', models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间')), 31 | ('belong_module', smart_selects.db_fields.GroupedForeignKey(group_field='belong_project', on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.module', verbose_name='所属模块')), 32 | ('belong_project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.project', verbose_name='所属项目')), 33 | ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='责任人')), 34 | ], 35 | options={ 36 | 'verbose_name': '测试用例表', 37 | 'verbose_name_plural': '测试用例表', 38 | }, 39 | ), 40 | ] 41 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/case_suite_execute_record.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}测试集合执行结果{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% for case_suite_execute_record in case_suite_execute_records %} 24 | 25 | 26 | 27 | 28 | {% if case_suite_execute_record.status %} 29 | 30 | {% else %} 31 | 32 | {% endif %} 33 | 34 | {% ifequal case_suite_execute_record.test_result '成功' %} 35 | 36 | {% else %} 37 | 38 | {% endifequal %} 39 | 40 | 41 | 42 | 43 | {% endfor %} 44 | 45 | 46 | 47 |
id测试集合名称延迟执行时间执行状态测试结果测试结果统计创建者创建时间
{{ case_suite_execute_record.id }}{{ case_suite_execute_record.case_suite.suite_desc }}{{ case_suite_execute_record.run_time_interval }}执行完毕待执行{{ case_suite_execute_record.test_result}}{{ case_suite_execute_record.test_result}}测试结果统计{{ case_suite_execute_record.creator }}{{ case_suite_execute_record.create_time|date:"Y-n-d H:i" }}
48 |
49 | 50 | {# 实现分页标签的代码 #} 51 | {# 这里使用 bootstrap 渲染页面 #} 52 |
53 | 69 |
70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/migrations/0011_casesuiteexecuterecord_casesuitetestcaseexecuterecord.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-07-17 22:37 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('interfacetestplatform', '0010_alter_testcaseexecuteresult_exception_info'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='CaseSuiteExecuteRecord', 16 | fields=[ 17 | ('id', models.AutoField(primary_key=True, serialize=False)), 18 | ('run_time_interval', models.IntegerField(default=0, null=True, verbose_name='延迟时间')), 19 | ('status', models.IntegerField(default=0, null=True, verbose_name='执行状态')), 20 | ('test_result', models.CharField(blank=True, max_length=50, null=True)), 21 | ('creator', models.CharField(blank=True, max_length=50, null=True)), 22 | ('create_time', models.DateTimeField(auto_now=True, verbose_name='创建时间')), 23 | ('execute_start_time', models.CharField(blank=True, max_length=300, null=True, verbose_name='执行开始时间')), 24 | ('case_suite', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.casesuite', verbose_name='测试集合')), 25 | ], 26 | ), 27 | migrations.CreateModel( 28 | name='CaseSuiteTestCaseExecuteRecord', 29 | fields=[ 30 | ('id', models.AutoField(primary_key=True, serialize=False)), 31 | ('status', models.IntegerField(default=0, null=True, verbose_name='执行状态')), 32 | ('exception_info', models.CharField(blank=True, max_length=2048, null=True)), 33 | ('request_data', models.CharField(max_length=1024, null=True, verbose_name='请求体')), 34 | ('response_data', models.CharField(max_length=1024, null=True, verbose_name='响应字符串')), 35 | ('execute_result', models.CharField(max_length=1024, null=True, verbose_name='执行结果')), 36 | ('extract_var', models.CharField(max_length=1024, null=True, verbose_name='关联参数')), 37 | ('last_time_response_data', models.CharField(max_length=1024, null=True, verbose_name='上一次响应字符串')), 38 | ('execute_total_time', models.CharField(max_length=1024, null=True, verbose_name='执行耗时')), 39 | ('execute_start_time', models.CharField(blank=True, max_length=300, null=True, verbose_name='执行开始时间')), 40 | ('execute_end_time', models.CharField(blank=True, max_length=300, null=True, verbose_name='执行结束时间')), 41 | ('case_suite_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.casesuiteexecuterecord', verbose_name='测试集合执行记录')), 42 | ('test_case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='interfacetestplatform.testcase', verbose_name='测试用例')), 43 | ], 44 | ), 45 | ] 46 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/test_case_execute_records.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}用例执行记录{% endblock %} 4 | {% block content %} 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {% for testrecord in test_case_execute_records %} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {% ifequal testrecord.execute_result '成功' %} 35 | 36 | {% else %} 37 | 38 | {% endifequal %} 39 | 40 | {% if testrecord.exception_info %} 41 | 42 | {% else %} 43 | 44 | {% endif %} 45 | 46 | 47 | 48 | 49 | 50 | {% endfor %} 51 | 52 | 53 |
id名称请求数据执行返回结果操作断言内容执行结果异常信息请求后提取变量开始时间执行耗时(ms)
{{ testrecord.id }}{{ testrecord.belong_test_case.case_name }}{{ testrecord.request_data }}{{ testrecord.response_data }}对比差异{{ testrecord.belong_test_case.assert_key }}{{ testrecord.execute_result}}{{ testrecord.execute_result}}显示异常信息{{ testrecord.extract_var }}{{ testrecord.execute_start_time }}{{ testrecord.execute_total_time }}
54 | 55 | {# 实现分页标签的代码 #} 56 | {# 这里使用 bootstrap 渲染页面 #} 57 |
58 | 74 |
75 |
76 | {% endblock %} 77 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% load static %} 4 | 5 | 6 | 7 | 8 | 9 | {% block title %}base{% endblock %} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | {% block css %}{% endblock %} 22 | 23 | 24 | 59 | 60 | {% block content %}{% endblock %} 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/suite_case_execute_record.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}用例集合用例执行结果{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% for case_execute_record in suite_case_execute_records %} 29 | 30 | 31 | 32 | 33 | {% if case_execute_record.status %} 34 | 35 | {% else %} 36 | 37 | {% endif %} 38 | 39 | 40 | 41 | 42 | 43 | {% ifequal case_execute_record.execute_result '成功' %} 44 | 45 | {% else %} 46 | 47 | {% endifequal %} 48 | 49 | {% if case_execute_record.exception_info %} 50 | 51 | {% else %} 52 | 53 | {% endif %} 54 | 55 | 56 | 57 | 58 | 59 | {% endfor %} 60 | 61 | 62 | 63 |
执行id集合名称用例名称状态请求数据执行返回结果操作断言内容执行结果异常信息请求后提取变量开始时间执行耗时(ms)
{{ case_execute_record.id }}{{ case_execute_record.case_suite_record.case_suite.suite_desc }}{{ case_execute_record.test_case.case_name }}执行完毕待执行{{ case_execute_record.request_data }}{{ case_execute_record.response_data }}对比差异{{ case_execute_record.test_case.assert_key }}{{ case_execute_record.execute_result}}{{ case_execute_record.execute_result}}显示异常{{ case_execute_record.extract_var }}{{ case_execute_record.execute_start_time }}{{ case_execute_record.execute_total_time }}
64 |
65 | 66 | {# 实现分页标签的代码 #} 67 | {# 这里使用 bootstrap 渲染页面 #} 68 |
69 |
83 | {% endblock %} 84 | 85 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/add_case_in_suite.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}添加测试用例{% endblock %} 4 | {% block content %} 5 | 6 | 42 |
43 | {% csrf_token %} 44 | 45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {% for test_case in test_cases %} 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | {% endfor %} 72 | 73 |
id用例名称所属项目所属模块编写人员创建时间更新时间创建用例用户名
{{ test_case.id }}{{ test_case.case_name }}{{ test_case.belong_project.name }}{{ test_case.belong_module.name }}{{ test_case.maintainer }}{{ test_case.created_time|date:"Y-n-d H:i" }}{{ test_case.updated_time|date:"Y-n-d H:i" }}{{ test_case.user.username }}
74 |
75 |
76 | {# 实现分页标签的代码 #} 77 | {# 这里使用 bootstrap 渲染页面 #} 78 |
79 | 93 |
94 | {% endblock %} 95 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/test_case.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}测试用例{% endblock %} 4 | 5 | {% block content %} 6 | 42 | 43 |
44 | {% csrf_token %} 45 | 46 | 运行环境: 47 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {% for test_case in test_cases %} 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | {% endfor %} 81 | 82 |
全选用例名称所属项目所属模块接口地址请求方式请求数据断言key提取变量表达式
{{ test_case.id }}{{ test_case.case_name }}{{ test_case.belong_project.name }}{{ test_case.belong_module.name }}{{ test_case.uri }}{{ test_case.request_method }}{{ test_case.request_data }}{{ test_case.assert_key }}{{ test_case.extract_var }}
83 | 84 |
85 |
86 | {# 实现分页标签的代码 #} 87 | {# 这里使用 bootstrap 渲染页面 #} 88 |
89 | 105 |
106 | {% endblock %} 107 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/case_suite.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}用例集合{% endblock %} 4 | {% block content %} 5 | 40 | 41 |
42 | {% csrf_token %} 43 | 延迟执行的时间(单位:秒): 44 | 45 | 运行环境: 46 | 50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {% for case_suite in case_suites %} 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | {% endfor %} 80 | 81 |
全选id测试集合名称创建者创建时间查看/删除测试用例添加测试用例用例集合执行结果
{{ case_suite.id }}{{ case_suite.suite_desc }}{{ case_suite.creator }}{{ case_suite.create_time|date:"Y-n-d H:i" }}查看/删除测试用例添加测试用例查看用例集合执行结果
82 |
83 |
84 | 85 | {# 实现分页标签的代码 #} 86 | {# 这里使用 bootstrap 渲染页面 #} 87 |
88 | 104 |
105 | {% endblock %} 106 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/case_suite_statistics.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}测试集合结果统计{% endblock %} 4 | {% block content %} 5 | 6 | 7 | 8 |

9 | 用例集合执行结果统计:成功 {{ success_num }} 次,失败 {{ fail_num }} 次 10 |

11 |

12 | 13 | 14 | 58 | 59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {% for case_suite_record in case_suite_records %} 75 | 76 | 77 | 78 | 79 | {% if case_suite_record.status %} 80 | 81 | {% else %} 82 | 83 | {% endif %} 84 | 85 | 86 | 87 | 88 | {% endfor %} 89 | 90 | 91 |
id测试集合名称延迟执行时间执行状态测试结果创建者创建时间
{{ case_suite_record.id }}{{ case_suite_record.case_suite.suite_desc }}{{ case_suite_record.run_time_interval }}执行完毕待执行{{ case_suite_record.test_result|default_if_none:"" }}{{ case_suite_record.creator }}{{ case_suite_record.create_time|date:"Y-n-d H:i" }}
92 |
93 | 94 | {# 实现分页标签的代码 #} 95 | {# 这里使用 bootstrap 渲染页面 #} 96 |
97 | 113 |
114 | 115 | 116 | {% endblock %} 117 | -------------------------------------------------------------------------------- /InterfaceAutoTest/InterfaceAutoTest/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for InterfaceAutoTest project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | import os 15 | 16 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 17 | BASE_DIR = Path(__file__).resolve().parent.parent 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = 'django-insecure-tby$dbfz$tm%)q^sy4+c28xn2h4&5$88570_e_kpfp&q!cq$v#' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 'interfacetestplatform', 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | 'django.middleware.security.SecurityMiddleware', 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | # 添加中间件LocaleMiddleware,使用中文 49 | 'django.middleware.locale.LocaleMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'InterfaceAutoTest.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'InterfaceAutoTest.wsgi.application' 75 | 76 | 77 | # Database 78 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 79 | 80 | # DATABASES = { 81 | # 'default': { 82 | # 'ENGINE': 'django.db.backends.sqlite3', 83 | # 'NAME': BASE_DIR / 'db.sqlite3', 84 | # } 85 | # } 86 | DATABASES = { 87 | 'default': { 88 | 'ENGINE': 'django.db.backends.mysql', 89 | 'NAME': 'interface_platform', # 库名 90 | 'USER': 'root', # 数据库账号 91 | 'PASSWORD': 'admin', # 数据库密码 92 | 'HOST': '127.0.0.1', # 数据库IP 93 | 'PORT': '3306', # 数据库端口 94 | } 95 | } 96 | 97 | 98 | # Password validation 99 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 100 | 101 | AUTH_PASSWORD_VALIDATORS = [ 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 104 | }, 105 | { 106 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 107 | }, 108 | { 109 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 110 | }, 111 | { 112 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 113 | }, 114 | ] 115 | 116 | 117 | # Internationalization 118 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 119 | 120 | LANGUAGE_CODE = 'en-us' 121 | 122 | # TIME_ZONE = 'UTC' 123 | 124 | USE_I18N = True 125 | 126 | USE_L10N = True 127 | 128 | # USE_TZ = True 129 | 130 | TIME_ZONE = 'Asia/Shanghai' 131 | 132 | USE_TZ = False 133 | 134 | # Static files (CSS, JavaScript, Images) 135 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 136 | 137 | STATIC_URL = '/static/' 138 | 139 | # Default primary key field type 140 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 141 | 142 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 143 | 144 | STATICFILES_DIRS = [ 145 | os.path.join(BASE_DIR, "static"), 146 | ] 147 | 148 | LOGIN_URL = '/login/' 149 | 150 | redis_port = 6379 -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/show_and_delete_case_in_suite.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}查看/删除测试用例{% endblock %} 4 | {% block content %} 5 | 6 | 45 | 46 |

测试集合名称:{{case_suite.suite_desc}}

47 |
48 |
49 | {% csrf_token %} 50 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {% for test_case in test_cases %} 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | {% endfor %} 81 | 82 |
全选用例id用例名称所属项目所属模块编写人员创建时间更新时间创建用例用户名
{{ test_case.test_case.id }}{{ test_case.test_case.case_name }}{{ test_case.test_case.belong_project.name }}{{ test_case.test_case.belong_module.name }}{{ test_case.test_case.maintainer }}{{ test_case.test_case.created_time|date:"Y-n-d H:i" }}{{ test_case.test_case.updated_time|date:"Y-n-d H:i" }}{{ test_case.test_case.user.username }}
83 |
84 |
85 | 86 | {# 实现分页标签的代码 #} 87 | {# 这里使用 bootstrap 渲染页面 #} 88 |
89 | 105 |
106 |
107 |
108 | {% endblock %} 109 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/templates/suite_case_statistics.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | {% block title %}用例集合单次执行结果统计{% endblock %} 4 | {% block content %} 5 | 6 | 7 | 8 |

9 | 用例集合执行结果统计:成功 {{ success_num }} 次,失败 {{ fail_num }} 次 10 |

11 |

12 | 13 | 14 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | {% for suite_case in suite_case_records %} 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | {% endfor %} 88 | 89 |
测试集合名称用例id用例名称所属项目所属模块编写人员创建时间更新时间创建用例用户名
{{suite_case.case_suite_record.case_suite.suite_desc}}{{ suite_case.id }}{{ suite_case.test_case.case_name }}{{ suite_case.test_case.belong_project.name }}{{ suite_case.test_case.belong_module.name }}{{ suite_case.test_case.maintainer }}{{ suite_case.test_case.created_time|date:"Y-n-d H:i" }}{{ suite_case.test_case.updated_time|date:"Y-n-d H:i" }}{{ suite_case.test_case.user }}
90 | 91 | 92 | 93 | {# 实现分页标签的代码 #} 94 | {# 这里使用 bootstrap 渲染页面 #} 95 |
96 | 112 |
113 | 114 | 115 | 116 | {% endblock %} -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/utils/data_process.py: -------------------------------------------------------------------------------- 1 | import re 2 | import hashlib 3 | import os 4 | import json 5 | import traceback 6 | import redis 7 | from InterfaceAutoTest.settings import redis_port 8 | 9 | 10 | # 连接redis 11 | pool = redis.ConnectionPool(host='127.0.0.1', port=redis_port, decode_responses=True) 12 | redis_obj = redis.Redis(connection_pool=pool) 13 | 14 | 15 | # 初始化框架工程中的全局变量,存储在测试数据中的唯一值数据 16 | # 框架工程中若要使用字典中的任意一个变量,则每次使用后,均需要将字典中的value值进行加1操作。 17 | def get_unique_number_value(unique_number): 18 | data = None 19 | try: 20 | redis_value = redis_obj.get(unique_number) # {"unique_number":666} 21 | if redis_value: 22 | data = redis_value 23 | print("全局唯一数当前生成的值是:%s" % data) 24 | # 把redis中key为unique_number的值进行加一操作,以便下提取时保持唯一 25 | redis_obj.set(unique_number, int(redis_value) + 1) 26 | else: 27 | data = 20300 # 初始化递增数值 28 | redis_obj.set(unique_number, data) 29 | except Exception as e: 30 | print("获取全局唯一数变量值失败,请求的全局唯一数变量是%s,异常原因如下:%s" % (unique_number, traceback.format_exc())) 31 | data = None 32 | finally: 33 | return data 34 | 35 | 36 | def md5(s): 37 | m5 = hashlib.md5() 38 | m5.update(s.encode("utf-8")) 39 | md5_value = m5.hexdigest() 40 | return md5_value 41 | 42 | 43 | # 请求数据预处理:参数化、函数化 44 | # 将请求数据中包含的${变量名}的字符串部分,替换为唯一数或者全局变量字典中对应的全局变量 45 | def data_preprocess(global_key, requestData): 46 | try: 47 | # 匹配注册用户名参数,即"${unique_num...}"的格式,并取出本次请求的随机数供后续接口的用户名参数使用 48 | if re.search(r"\$\{unique_num\d+\}", requestData): 49 | var_name = re.search(r"\$\{(unique_num\d+)\}", requestData).group(1) # 获取用户名参数 50 | print("用户名变量:%s" % var_name) 51 | var_value = get_unique_number_value(var_name) 52 | print("用户名变量值:%s" % var_value) 53 | requestData = re.sub(r"\$\{unique_num\d+\}", str(var_value), requestData) 54 | var_name = var_name.split("_")[1] 55 | print("关联的用户名变量:%s" % var_name) 56 | # "xxxkey" :"{'var_name':var_value}" 57 | global_var = json.loads(os.environ[global_key]) 58 | global_var[var_name] = var_value 59 | os.environ[global_key] = json.dumps(global_var) 60 | print("用户名唯一数参数化后的全局变量【os.environ[global_key]】:{}".format(os.environ[global_key])) 61 | # 函数化,如密码加密"${md5(...)}"的格式 62 | if re.search(r"\$\{\w+\(.+\)\}", requestData): 63 | var_pass = re.search(r"\$\{(\w+\(.+\))\}", requestData).group(1) # 获取密码参数 64 | print("需要函数化的变量:%s" % var_pass) 65 | print("函数化后的结果:%s" % eval(var_pass)) 66 | requestData = re.sub(r"\$\{\w+\(.+\)\}", eval(var_pass), requestData) # 将requestBody里面的参数内容通过eval修改为实际变量值 67 | print("函数化后的请求数据:%s" % requestData) # requestBody是拿到的请求时发送的数据 68 | # 其余变量参数化 69 | if re.search(r"\$\{(\w+)\}", requestData): 70 | print("需要参数化的变量:%s" % (re.findall(r"\$\{(\w+)\}", requestData))) 71 | for var_name in re.findall(r"\$\{(\w+)\}", requestData): 72 | requestData = re.sub(r"\$\{%s\}" % var_name, str(json.loads(os.environ[global_key])[var_name]), requestData) 73 | print("变量参数化后的最终请求数据:%s" % requestData) 74 | print("数据参数后的最终全局变量【os.environ[global_key]】:{}".format(os.environ[global_key])) 75 | return 0, requestData, "" 76 | except Exception as e: 77 | print("请求数据预处理发生异常,error:{}".format(traceback.format_exc())) 78 | return 1, {}, traceback.format_exc() 79 | 80 | 81 | # 响应数据提取关联参数 82 | def data_postprocess(global_key, response_data, extract_var): 83 | print("需提取的关联变量:%s" % extract_var) 84 | var_name = extract_var.split("||")[0] 85 | print("关联变量名:%s" % var_name) 86 | regx_exp = extract_var.split("||")[1] 87 | print("关联变量正则:%s" % regx_exp) 88 | if re.search(regx_exp, response_data): 89 | global_vars = json.loads(os.environ[global_key]) 90 | print("关联前的全局变量:{}".format(global_vars)) 91 | var_value = re.search(regx_exp, response_data).group(1) 92 | global_vars[var_name] = var_value 93 | os.environ[global_key] = json.dumps(global_vars) 94 | print("关联前的全局变量:{}".format(os.environ[global_key])) 95 | return var_name+":"+var_value 96 | 97 | 98 | # 响应数据 断言处理 99 | def assert_result(response_obj, key_word): 100 | try: 101 | # 多个断言关键字 102 | if '&&' in key_word: 103 | key_word_list = key_word.split('&&') 104 | print("断言关键字列表:%s" % key_word_list) 105 | # 断言结果标识符 106 | flag = True 107 | exception_info = '' 108 | # 遍历分隔出来的断言关键词列表 109 | for key_word in key_word_list: 110 | # 如果断言词非空,则进行断言 111 | if key_word: 112 | # 没查到断言词则认为是断言失败 113 | if not (key_word in json.dumps(response_obj.json(), ensure_ascii=False)): 114 | print("断言关键字【{}】匹配失败".format(key_word)) 115 | flag = False # 只要有一个断言词匹配失败,则整个接口断言失败 116 | exception_info = "keyword:{} not matched from response, assert failed".format(key_word) 117 | else: 118 | print("断言关键字【{}】匹配成功".format(key_word)) 119 | if flag: 120 | print("接口断言成功!") 121 | else: 122 | print("接口断言失败!") 123 | return flag, exception_info 124 | # 单个断言关键字 125 | else: 126 | if key_word in json.dumps(response_obj.json(), ensure_ascii=False): 127 | print("接口断言【{}】匹配成功!".format(key_word)) 128 | return True, '' 129 | else: 130 | print("接口断言【{}】匹配失败!".format(key_word)) 131 | return False, '' 132 | except Exception as e: 133 | return False, traceback.format_exc() 134 | 135 | 136 | # 测试代码 137 | if __name__ == "__main__": 138 | print(get_unique_number_value("unique_num1")) 139 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from smart_selects.db_fields import GroupedForeignKey # pip install django-smart-selects:后台级联选择 3 | from django.contrib.auth.models import User 4 | 5 | 6 | # 项目 7 | class Project(models.Model): 8 | id = models.AutoField(primary_key=True) 9 | name = models.CharField('项目名称', max_length=50, unique=True, null=False) 10 | proj_owner = models.CharField('项目负责人', max_length=20, null=False) 11 | test_owner = models.CharField('测试负责人', max_length=20, null=False) 12 | dev_owner = models.CharField('开发负责人', max_length=20, null=False) 13 | desc = models.CharField('项目描述', max_length=100, null=True) 14 | create_time = models.DateTimeField('项目创建时间', auto_now_add=True) 15 | update_time = models.DateTimeField('项目更新时间', auto_now=True, null=True) 16 | 17 | def __str__(self): 18 | return self.name 19 | 20 | class Meta: 21 | verbose_name = '项目信息表' 22 | verbose_name_plural = '项目信息表' 23 | 24 | 25 | # 模块 26 | class Module(models.Model): 27 | id = models.AutoField(primary_key=True) 28 | name = models.CharField('模块名称', max_length=50, null=False) 29 | belong_project = models.ForeignKey(Project, on_delete=models.CASCADE) 30 | test_owner = models.CharField('测试负责人', max_length=50, null=False) 31 | desc = models.CharField('简要描述', max_length=100, null=True) 32 | create_time = models.DateTimeField('创建时间', auto_now_add=True) 33 | update_time = models.DateTimeField('更新时间', auto_now=True, null=True) 34 | 35 | def __str__(self): 36 | return self.name 37 | 38 | class Meta: 39 | verbose_name = '模块信息表' 40 | verbose_name_plural = '模块信息表' 41 | 42 | 43 | # 测试用例 44 | class TestCase(models.Model): 45 | id = models.AutoField(primary_key=True) 46 | case_name = models.CharField('用例名称', max_length=50, null=False) # 如 register 47 | belong_project = models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name='所属项目') 48 | belong_module = GroupedForeignKey(Module, "belong_project", on_delete=models.CASCADE, verbose_name='所属模块') 49 | request_data = models.CharField('请求数据', max_length=1024, null=False, default='') 50 | uri = models.CharField('接口地址', max_length=1024, null=False, default='') 51 | assert_key = models.CharField('断言内容', max_length=1024, null=True) 52 | maintainer = models.CharField('编写人员', max_length=1024, null=False, default='') 53 | extract_var = models.CharField('提取变量表达式', max_length=1024, null=True) # 示例:userid||userid": (\d+) 54 | request_method = models.CharField('请求方式', max_length=1024, null=True) 55 | status = models.IntegerField(null=True, help_text="0:表示有效,1:表示无效,用于软删除") 56 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 57 | updated_time = models.DateTimeField('更新时间', auto_now=True, null=True) 58 | user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='责任人', null=True) 59 | 60 | def __str__(self): 61 | return self.case_name 62 | 63 | class Meta: 64 | verbose_name = '测试用例表' 65 | verbose_name_plural = '测试用例表' 66 | 67 | 68 | # 用例集合 69 | class CaseSuite(models.Model): 70 | id = models.AutoField(primary_key=True) 71 | suite_desc = models.CharField('用例集合描述', max_length=100, blank=True, null=True) 72 | if_execute = models.IntegerField(verbose_name='是否执行', null=False, default=0, help_text='0:执行;1:不执行') 73 | test_case_model = models.CharField('测试执行模式', max_length=100, blank=True, null=True, help_text='data/keyword') 74 | creator = models.CharField(max_length=50, blank=True, null=True) 75 | create_time = models.DateTimeField('创建时间', auto_now=True) # 创建时间-自动获取当前时间 76 | 77 | class Meta: 78 | verbose_name = "用例集合表" 79 | verbose_name_plural = '用例集合表' 80 | 81 | 82 | # 用例集合关联用例 83 | class SuiteCase(models.Model): 84 | id = models.AutoField(primary_key=True) 85 | case_suite = models.ForeignKey(CaseSuite, on_delete=models.CASCADE, verbose_name='用例集合') 86 | test_case = models.ForeignKey(TestCase, on_delete=models.CASCADE, verbose_name='测试用例') 87 | status = models.IntegerField(verbose_name='是否有效', null=False, default=1, help_text='0:有效,1:无效') 88 | create_time = models.DateTimeField('创建时间', auto_now=True) # 创建时间-自动获取当前时间 89 | 90 | 91 | # 接口服务器配置 92 | class InterfaceServer(models.Model): 93 | id = models.AutoField(primary_key=True) 94 | env = models.CharField('环境', max_length=50, null=False, default='') 95 | ip = models.CharField('ip', max_length=50, null=False, default='') 96 | port = models.CharField('端口', max_length=100, null=False, default='') 97 | remark = models.CharField('备注', max_length=100, null=True) 98 | create_time = models.DateTimeField('创建时间', auto_now_add=True) 99 | update_time = models.DateTimeField('更新时间', auto_now=True, null=True) 100 | 101 | def __str__(self): 102 | return self.env 103 | 104 | class Meta: 105 | verbose_name = '接口地址配置表' 106 | verbose_name_plural = '接口地址配置表' 107 | 108 | 109 | # 测试用例执行记录 110 | class TestCaseExecuteResult(models.Model): 111 | id = models.AutoField(primary_key=True) 112 | belong_test_case = GroupedForeignKey(TestCase, "belong_test_case", on_delete=models.CASCADE, verbose_name='所属用例') 113 | status = models.IntegerField(null=True, help_text="0:表示未执行,1:表示已执行") 114 | exception_info = models.CharField(max_length=2048, blank=True, null=True) 115 | request_data = models.CharField('请求体', max_length=1024, null=True) # {"code": "00", "userid": 22889} 116 | response_data = models.CharField('响应字符串', max_length=1024, null=True) # {"code": "00", "userid": 22889} 117 | execute_result = models.CharField('执行结果', max_length=1024, null=True) # 成功/失败 118 | extract_var = models.CharField('关联参数', max_length=1024, null=True) # 响应成功后提取变量 119 | last_time_response_data = models.CharField('上一次响应字符串', max_length=1024, null=True) # {"code": "00", "userid": 22889} 120 | execute_total_time = models.CharField('执行耗时', max_length=1024, null=True) 121 | execute_start_time = models.CharField('执行开始时间', max_length=300, blank=True, null=True) 122 | execute_end_time = models.CharField('执行结束时间', max_length=300, blank=True, null=True) 123 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 124 | updated_time = models.DateTimeField('更新时间', auto_now=True, null=True) 125 | 126 | def __str__(self): 127 | return str(self.id) 128 | 129 | class Meta: 130 | verbose_name = '用例执行结果记录表' 131 | verbose_name_plural = '用例执行结果记录表' 132 | 133 | 134 | # 用例集合的执行记录 135 | class CaseSuiteExecuteRecord(models.Model): 136 | id = models.AutoField(primary_key=True) 137 | case_suite = models.ForeignKey(CaseSuite, on_delete=models.CASCADE, verbose_name='测试集合') 138 | run_time_interval = models.IntegerField(verbose_name='延迟时间', null=True, default=0) 139 | status = models.IntegerField(verbose_name='执行状态', null=True, default=0) 140 | test_result = models.CharField(max_length=50, blank=True, null=True) 141 | creator = models.CharField(max_length=50, blank=True, null=True) 142 | create_time = models.DateTimeField('创建时间', auto_now=True) # 创建时间-自动获取当前时间 143 | execute_start_time = models.CharField('执行开始时间', max_length=300, blank=True, null=True) 144 | 145 | 146 | # 用例集合下的用例执行记录 147 | class CaseSuiteTestCaseExecuteRecord(models.Model): 148 | id = models.AutoField(primary_key=True) 149 | case_suite_record = models.ForeignKey(CaseSuiteExecuteRecord, on_delete=models.CASCADE, verbose_name='测试集合执行记录') 150 | test_case = models.ForeignKey(TestCase, on_delete=models.CASCADE, verbose_name='测试用例') 151 | status = models.IntegerField(verbose_name='执行状态', null=True, default=0) 152 | exception_info = models.CharField(max_length=2048, blank=True, null=True) 153 | request_data = models.CharField('请求体', max_length=1024, null=True) # {"code": "00", "userid": 22889} 154 | response_data = models.CharField('响应字符串', max_length=1024, null=True) # {"code": "00", "userid": 22889} 155 | execute_result = models.CharField('执行结果', max_length=1024, null=True) # 成功/失败 156 | extract_var = models.CharField('关联参数', max_length=1024, null=True) # 响应成功后提取变量 157 | last_time_response_data = models.CharField('上一次响应字符串', max_length=1024, 158 | null=True) # {"code": "00", "userid": 22889} 159 | execute_total_time = models.CharField('执行耗时', max_length=1024, null=True) 160 | execute_start_time = models.CharField('执行开始时间', max_length=300, blank=True, null=True) 161 | execute_end_time = models.CharField('执行结束时间', max_length=300, blank=True, null=True) 162 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/task.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | from celery import shared_task 3 | import time 4 | import os 5 | import traceback 6 | import json 7 | from . import models 8 | from .utils.data_process import data_preprocess, assert_result, data_postprocess 9 | from .utils.request_process import request_process 10 | 11 | 12 | # 测试用例执行 13 | @shared_task 14 | def case_task(test_case_id_list, server_address): 15 | global_key = 'case' + str(int(time.time() * 100000)) 16 | os.environ[global_key] = '{}' 17 | print() 18 | print("全局变量标识符【global_key】: {}".format(global_key)) 19 | print("全局变量内容【os.environ[global_key]】: {}".format(os.environ[global_key])) 20 | for test_case_id in test_case_id_list: 21 | 22 | test_case = models.TestCase.objects.filter(id=int(test_case_id))[0] 23 | last_execute_record_data = models.TestCaseExecuteResult.objects.filter( 24 | belong_test_case_id=test_case_id).order_by('-id') 25 | if last_execute_record_data: 26 | last_time_execute_response_data = last_execute_record_data[0].response_data 27 | else: 28 | last_time_execute_response_data = '' 29 | print("上一次响应结果: {}".format(last_execute_record_data)) 30 | print("上一次响应时间: {}".format(last_time_execute_response_data)) 31 | execute_record = models.TestCaseExecuteResult.objects.create(belong_test_case=test_case) 32 | execute_record.last_time_response_data = last_time_execute_response_data 33 | # 获取当前用例上一次执行结果 34 | execute_record.save() 35 | 36 | test_case = models.TestCase.objects.filter(id=int(test_case_id))[0] 37 | print("\n######### 开始执行用例【{}】 #########".format(test_case)) 38 | execute_start_time = time.time() # 记录时间戳,便于计算总耗时(毫秒) 39 | execute_record.execute_start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(execute_start_time)) 40 | 41 | request_data = test_case.request_data 42 | extract_var = test_case.extract_var 43 | assert_key = test_case.assert_key 44 | interface_name = test_case.uri 45 | belong_project = test_case.belong_project 46 | belong_module = test_case.belong_module 47 | maintainer = test_case.maintainer 48 | request_method = test_case.request_method 49 | print("初始请求数据: {}".format(request_data)) 50 | print("关联参数: {}".format(extract_var)) 51 | print("断言关键字: {}".format(assert_key)) 52 | print("接口名称: {}".format(interface_name)) 53 | print("所属项目: {}".format(belong_project)) 54 | print("所属模块: {}".format(belong_module)) 55 | print("用例维护人: {}".format(maintainer)) 56 | print("请求方法: {}".format(request_method)) 57 | url = "{}{}".format(server_address, interface_name) 58 | print("接口地址: {}".format(url)) 59 | code, request_data, error_msg = data_preprocess(global_key, str(request_data)) 60 | # 请求数据预处理异常,结束用例执行 61 | if code != 0: 62 | print("数据处理异常,error: {}".format(error_msg)) 63 | execute_record.execute_result = "失败" 64 | execute_record.status = 1 65 | execute_record.exception_info = error_msg 66 | execute_end_time = time.time() 67 | execute_record.execute_end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(execute_end_time)) 68 | execute_record.execute_total_time = int(execute_end_time - execute_start_time) * 1000 69 | execute_record.save() 70 | return 71 | # 记录请求预处理结果 72 | else: 73 | execute_record.request_data = request_data 74 | # 调用接口 75 | try: 76 | res_data = request_process(url, request_method, json.loads(request_data)) 77 | print("响应数据: {}".format(json.dumps(res_data.json(), ensure_ascii=False))) # ensure_ascii:兼容中文 78 | result_flag, exception_info = assert_result(res_data, assert_key) 79 | # 结果记录保存 80 | if result_flag: 81 | print("用例【%s】执行成功!" % test_case) 82 | execute_record.execute_result = "成功" 83 | if extract_var.strip() != "None": 84 | var_value = data_postprocess(global_key, json.dumps(res_data.json(), ensure_ascii=False), extract_var) 85 | execute_record.extract_var = var_value 86 | else: 87 | print("用例【%s】执行失败!" % test_case) 88 | execute_record.execute_result = "失败" 89 | execute_record.exception_info = exception_info 90 | execute_record.response_data = json.dumps(res_data.json(), ensure_ascii=False) 91 | execute_record.status = 1 92 | execute_end_time = time.time() 93 | execute_record.execute_end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(execute_end_time)) 94 | print("执行结果结束时间: {}".format(execute_record.execute_end_time)) 95 | execute_record.execute_total_time = int((execute_end_time - execute_start_time) * 1000) 96 | print("用例执行耗时: {}".format(execute_record.execute_total_time)) 97 | execute_record.save() 98 | except Exception as e: 99 | print("接口请求异常,error: {}".format(traceback.format_exc())) 100 | execute_record.execute_result = "失败" 101 | execute_record.exception_info = traceback.format_exc() 102 | execute_record.status = 1 103 | execute_end_time = time.time() 104 | execute_record.execute_end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(execute_end_time)) 105 | print("执行结果结束时间: {}".format(execute_record.execute_end_time)) 106 | execute_record.execute_total_time = int(execute_end_time - execute_start_time) * 1000 107 | print("用例执行耗时: {} 毫秒".format(execute_record.execute_total_time)) 108 | execute_record.save() 109 | 110 | 111 | # 用例集合执行 112 | @shared_task 113 | def suite_task(case_suite_record, case_suite, server_address): 114 | global_key = case_suite.suite_desc + str(int(time.time() * 100000)) 115 | # global_vars = {"{}".format(global_key): {}} 116 | os.environ[global_key] = '{}' 117 | print("global_key: {}".format(global_key)) 118 | print("os.environ[global_key]: {}".format(os.environ[global_key])) 119 | case_suite_test_cases = models.SuiteCase.objects.filter(case_suite=case_suite).order_by('id') 120 | print("用例集合的测试用例列表: {}".format(case_suite_test_cases)) 121 | case_suite_record.test_result = "成功" 122 | case_suite_record.execute_start_time = time.strftime("%Y-%m-%d %H:%M:%S") 123 | 124 | for case_suite_test_case in case_suite_test_cases: 125 | test_case = case_suite_test_case.test_case 126 | print("\n######### 开始执行用例【{}】 #########".format(test_case)) 127 | last_execute_record_data = models.CaseSuiteTestCaseExecuteRecord.objects.filter( 128 | test_case_id=test_case.id).order_by('-id') 129 | if last_execute_record_data: 130 | last_time_execute_response_data = last_execute_record_data[0].response_data 131 | else: 132 | last_time_execute_response_data = '' 133 | print("上一次响应结果: {}".format(last_execute_record_data)) 134 | print("上一次响应时间: {}".format(last_time_execute_response_data)) 135 | suite_case_execute_record = models.CaseSuiteTestCaseExecuteRecord.objects.create(case_suite_record=case_suite_record, 136 | test_case=test_case) 137 | execute_start_time = time.time() # 记录时间戳,便于计算总耗时(毫秒) 138 | suite_case_execute_record.execute_start_time = time.strftime("%Y-%m-%d %H:%M:%S", 139 | time.localtime(execute_start_time)) 140 | print("用例集合开始执行时间: {}".format(suite_case_execute_record.execute_start_time)) 141 | suite_case_execute_record.last_time_response_data = last_time_execute_response_data 142 | suite_case_execute_record.save() 143 | request_data = test_case.request_data 144 | extract_var = test_case.extract_var 145 | assert_key = test_case.assert_key 146 | interface_name = test_case.uri 147 | belong_project = test_case.belong_project 148 | belong_module = test_case.belong_module 149 | maintainer = test_case.maintainer 150 | request_method = test_case.request_method 151 | print("初始请求数据: {}".format(request_data)) 152 | print("关联参数: {}".format(extract_var)) 153 | print("断言关键字: {}".format(assert_key)) 154 | print("接口名称: {}".format(interface_name)) 155 | print("所属项目: {}".format(belong_project)) 156 | print("所属模块: {}".format(belong_module)) 157 | print("用例维护人: {}".format(maintainer)) 158 | print("请求方法: {}".format(request_method)) 159 | url = "{}{}".format(server_address, interface_name) 160 | print("接口地址: {}".format(url)) 161 | # 请求数据预处理 162 | code, request_data, error_msg = data_preprocess(global_key, str(request_data)) 163 | # 请求数据预处理异常,结束用例执行 164 | if code != 0: 165 | print("数据处理异常,error: {}".format(error_msg)) 166 | suite_case_execute_record.execute_result = "失败" 167 | suite_case_execute_record.status = 1 168 | suite_case_execute_record.exception_info = error_msg 169 | execute_end_time = time.time() 170 | suite_case_execute_record.execute_end_time = time.strftime("%Y-%m-%d %H:%M:%S", 171 | time.localtime(execute_end_time)) 172 | suite_case_execute_record.execute_total_time = int(execute_end_time - execute_start_time) * 1000 173 | suite_case_execute_record.save() 174 | case_suite_record.test_result = "失败" 175 | # 记录请求预处理的结果 176 | suite_case_execute_record.request_data = request_data 177 | try: 178 | # 调用接口 179 | res_data = request_process(url, request_method, json.loads(request_data)) 180 | print("响应数据: {}".format(json.dumps(res_data.json(), ensure_ascii=False))) 181 | 182 | result_flag, exception_info = assert_result(res_data, assert_key) 183 | # 结果记录保存 184 | if result_flag: 185 | print("用例【%s】执行成功!" % test_case) 186 | suite_case_execute_record.execute_result = "成功" 187 | if extract_var.strip() != "None": 188 | var_value = data_postprocess(global_key, json.dumps(res_data.json(), ensure_ascii=False), 189 | extract_var) 190 | suite_case_execute_record.extract_var = var_value 191 | else: 192 | print("用例【%s】执行失败!" % test_case) 193 | suite_case_execute_record.execute_result = "失败" 194 | suite_case_execute_record.exception_info = exception_info 195 | case_suite_record.test_result = "失败" 196 | suite_case_execute_record.response_data = json.dumps(res_data.json(), ensure_ascii=False) 197 | suite_case_execute_record.status = 1 198 | execute_end_time = time.time() 199 | suite_case_execute_record.execute_end_time = time.strftime("%Y-%m-%d %H:%M:%S", 200 | time.localtime(execute_end_time)) 201 | suite_case_execute_record.execute_total_time = int((execute_end_time - execute_start_time) * 1000) 202 | print("用例执行耗时: {} 毫秒".format( 203 | suite_case_execute_record.execute_total_time)) 204 | suite_case_execute_record.save() 205 | except Exception as e: 206 | print("接口请求异常,error: {}".format(e)) 207 | suite_case_execute_record.execute_result = "失败" 208 | suite_case_execute_record.exception_info = traceback.format_exc() 209 | suite_case_execute_record.status = 1 210 | execute_end_time = time.time() 211 | suite_case_execute_record.execute_end_time = time.strftime("%Y-%m-%d %H:%M:%S", 212 | time.localtime(execute_end_time)) 213 | suite_case_execute_record.execute_total_time = int(execute_end_time - execute_start_time) * 1000 214 | print("用例集合执行总耗时: {} 毫秒".format(suite_case_execute_record.execute_total_time)) 215 | suite_case_execute_record.save() 216 | case_suite_record.test_result = "失败" 217 | 218 | case_suite_record.status = 1 # 执行完毕 219 | case_suite_record.save() 220 | -------------------------------------------------------------------------------- /InterfaceAutoTest/interfacetestplatform/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect, HttpResponse 2 | from django.contrib import auth # Django用户认证(Auth)组件一般用在用户的登录注册上,用于判断当前的用户是否合法 3 | from django.contrib.auth.decorators import login_required 4 | from django.core.paginator import Paginator, PageNotAnInteger, InvalidPage 5 | from .form import UserForm 6 | import traceback 7 | import json 8 | from . import models 9 | from .task import case_task, suite_task 10 | 11 | 12 | # 封装分页处理 13 | def get_paginator(request, data): 14 | paginator = Paginator(data, 10) # 默认每页展示10条数据 15 | # 获取 url 后面的 page 参数的值, 首页不显示 page 参数, 默认值是 1 16 | page = request.GET.get('page') 17 | try: 18 | paginator_pages = paginator.page(page) 19 | except PageNotAnInteger: 20 | # 如果请求的页数不是整数, 返回第一页。 21 | paginator_pages = paginator.page(1) 22 | except InvalidPage: 23 | # 如果请求的页数不存在, 重定向页面 24 | return HttpResponse('找不到页面的内容') 25 | return paginator_pages 26 | 27 | 28 | # 项目菜单项 29 | @login_required 30 | def project(request): 31 | print("request.user.is_authenticated: ", request.user.is_authenticated) 32 | projects = models.Project.objects.filter().order_by('-id') 33 | print("projects:", projects) 34 | return render(request, 'project.html', {'projects': get_paginator(request, projects)}) 35 | 36 | 37 | # 模块菜单项 38 | @login_required 39 | def module(request): 40 | if request.method == "GET": # 请求get时候,id倒序查询所有的模块数据 41 | modules = models.Module.objects.filter().order_by('-id') 42 | return render(request, 'module.html', {'modules': get_paginator(request, modules)}) 43 | else: # 否则就是Post请求,会根据输入内容,使用模糊的方式查找所有的项目 44 | proj_name = request.POST['proj_name'] 45 | projects = models.Project.objects.filter(name__contains=proj_name.strip()) 46 | projs = [proj.id for proj in projects] 47 | modules = models.Module.objects.filter(belong_project__in=projs) # 把项目中所有的模块都找出来 48 | return render(request, 'module.html', {'modules': get_paginator(request, modules), 'proj_name': proj_name}) 49 | 50 | 51 | # 获取测试用例执行的接口地址 52 | def get_server_address(env): 53 | if env: # 环境处理 54 | env_data = models.InterfaceServer.objects.filter(env=env[0]) 55 | print("env_data: {}".format(env_data)) 56 | if env_data: 57 | ip = env_data[0].ip 58 | port = env_data[0].port 59 | print("ip: {}, port: {}".format(ip, port)) 60 | server_address = "http://{}:{}".format(ip, port) 61 | print("server_address: {}".format(server_address)) 62 | return server_address 63 | else: 64 | return "" 65 | else: 66 | return "" 67 | 68 | 69 | # 测试用例菜单项 70 | @login_required 71 | def test_case(request): 72 | print("request.session['is_login']: {}".format(request.session['is_login'])) 73 | test_cases = "" 74 | if request.method == "GET": 75 | test_cases = models.TestCase.objects.filter().order_by('id') 76 | print("testcases: {}".format(test_cases)) 77 | elif request.method == "POST": 78 | print("request.POST: {}".format(request.POST)) 79 | test_case_id_list = request.POST.getlist('test_cases_list') 80 | env = request.POST.getlist('env') 81 | print("env: {}".format(env)) 82 | server_address = get_server_address(env) 83 | if not server_address: 84 | return HttpResponse("提交的运行环境为空,请选择环境后再提交!") 85 | if test_case_id_list: 86 | test_case_id_list.sort() 87 | print("test_case_id_list: {}".format(test_case_id_list)) 88 | print("获取到用例,开始用例执行") 89 | # 普通执行 90 | # case_task(test_case_id_list, server_address) 91 | # celery 执行 92 | case_task.apply_async((test_case_id_list, server_address)) 93 | else: 94 | print("运行测试用例失败") 95 | return HttpResponse("提交的运行测试用例为空,请选择用例后在提交!") 96 | test_cases = models.TestCase.objects.filter().order_by('id') 97 | return render(request, 'test_case.html', {'test_cases': get_paginator(request, test_cases)}) 98 | 99 | 100 | # 用例详情页 101 | @login_required 102 | def test_case_detail(request, test_case_id): 103 | test_case_id = int(test_case_id) 104 | test_case = models.TestCase.objects.get(id=test_case_id) 105 | print("test_case: {}".format(test_case)) 106 | print("test_case.id: {}".format(test_case.id)) 107 | print("test_case.belong_project: {}".format(test_case.belong_project)) 108 | 109 | return render(request, 'test_case_detail.html', {'test_case': test_case}) 110 | 111 | 112 | # 模块页展示测试用例 113 | @login_required 114 | def module_test_cases(request, module_id): 115 | module = "" 116 | if module_id: # 访问的时候,会从url中提取模块的id,根据模块id查询到模块数据,在模板中展现 117 | module = models.Module.objects.get(id=int(module_id)) 118 | test_cases = models.TestCase.objects.filter(belong_module=module).order_by('-id') 119 | print("test_case in module_test_cases: {}".format(test_cases)) 120 | return render(request, 'test_case.html', {'test_cases': get_paginator(request, test_cases)}) 121 | 122 | 123 | # 用例集合菜单项 124 | @login_required 125 | def case_suite(request): 126 | if request.method == "POST": 127 | count_down_time = 0 128 | if request.POST['delay_time']: 129 | print("输入的延迟时间是: {}".format(request.POST['delay_time'])) 130 | try: 131 | count_down_time = int(request.POST['delay_time']) 132 | except: 133 | print("输入的延迟时间是非数字!") 134 | else: 135 | print("没有输入延迟时间") 136 | env = request.POST.getlist('env') 137 | print("env: {}".format(env)) 138 | server_address = get_server_address(env) 139 | if not server_address: 140 | return HttpResponse("提交的运行环境为空,请选择环境后再提交!") 141 | case_suite_list = request.POST.getlist('case_suite_list') 142 | if case_suite_list: 143 | print("所需执行的用例集合列表:", case_suite_list) 144 | for suite_id in case_suite_list: 145 | test_suite = models.CaseSuite.objects.get(id=int(suite_id)) 146 | print("所需执行的用例集合: {}".format(test_suite)) 147 | username = request.user.username 148 | test_suite_record = models.CaseSuiteExecuteRecord.objects.create(case_suite=test_suite, 149 | run_time_interval=count_down_time, 150 | creator=username) 151 | # 普通执行 152 | # suite_task(test_suite_record, test_suite, server_address) 153 | # celery 执行 154 | suite_task.apply_async((test_suite_record, test_suite, server_address), countdown=count_down_time) 155 | else: 156 | print("运行测试集合用例失败") 157 | return HttpResponse("运行的测试集合为空,请选择测试集合后再运行!") 158 | case_suites = models.CaseSuite.objects.filter() 159 | return render(request, 'case_suite.html', {'case_suites': get_paginator(request, case_suites)}) 160 | 161 | 162 | # 用例集合-添加测试用例页 163 | @login_required 164 | def add_case_in_suite(request, suite_id): 165 | # 查询指定的用例集合 166 | case_suite = models.CaseSuite.objects.get(id=suite_id) 167 | # 根据id号查询所有的用例 168 | test_cases = models.TestCase.objects.filter().order_by('id') 169 | if request.method == "GET": 170 | print("test cases:", test_cases) 171 | elif request.method == "POST": 172 | test_cases_list = request.POST.getlist('testcases_list') 173 | # 如果页面勾选了用例 174 | if test_cases_list: 175 | print("勾选用例id:", test_cases_list) 176 | # 根据页面勾选的用例与查询出的所有用例一一比较 177 | for test_case in test_cases_list: 178 | test_case = models.TestCase.objects.get(id=int(test_case)) 179 | # 匹配成功则添加用例 180 | models.SuiteCase.objects.create(case_suite=case_suite, test_case=test_case) 181 | # 未勾选用例 182 | else: 183 | print("添加测试用例失败") 184 | return HttpResponse("添加的测试用例为空,请选择用例后再添加!") 185 | return render(request, 'add_case_in_suite.html', 186 | {'test_cases': get_paginator(request, test_cases), 'case_suite': case_suite}) 187 | 188 | 189 | # 用例集合页-查看/删除用例 190 | @login_required 191 | def show_and_delete_case_in_suite(request, suite_id): 192 | case_suite = models.CaseSuite.objects.get(id=suite_id) 193 | test_cases = models.SuiteCase.objects.filter(case_suite=case_suite) 194 | if request.method == "POST": 195 | test_cases_list = request.POST.getlist('test_cases_list') 196 | if test_cases_list: 197 | print("勾选用例:", test_cases_list) 198 | for test_case in test_cases_list: 199 | test_case = models.TestCase.objects.get(id=int(test_case)) 200 | models.SuiteCase.objects.filter(case_suite=case_suite, test_case=test_case).first().delete() 201 | else: 202 | print("测试用例删除失败") 203 | return HttpResponse("所选测试用例为空,请选择用例后再进行删除!") 204 | case_suite = models.CaseSuite.objects.get(id=suite_id) 205 | return render(request, 'show_and_delete_case_in_suite.html', 206 | {'test_cases': get_paginator(request, test_cases), 'case_suite': case_suite}) 207 | 208 | 209 | # 用例执行结果-菜单项 210 | @login_required 211 | def test_case_execute_record(request): 212 | test_case_execute_records = models.TestCaseExecuteResult.objects.filter().order_by('-id') 213 | return render(request, 'test_case_execute_records.html', {'test_case_execute_records': 214 | get_paginator(request, test_case_execute_records)}) 215 | 216 | 217 | # 用例执行结果-对比差异 218 | @login_required 219 | def case_result_diff(request, test_record_id): 220 | test_record_data = models.TestCaseExecuteResult.objects.get(id=test_record_id) 221 | print("用例执行结果记录: {}".format(test_record_data)) 222 | present_response = test_record_data.response_data 223 | if present_response: 224 | present_response = json.dumps(json.loads(present_response), sort_keys=True, indent=4, 225 | ensure_ascii=False) # 中文字符不转ascii编码 226 | print("当前响应结果: {}".format(present_response)) 227 | last_time_execute_response = test_record_data.last_time_response_data 228 | if last_time_execute_response: 229 | last_time_execute_response = json.dumps(json.loads(last_time_execute_response), sort_keys=True, indent=4, 230 | ensure_ascii=False) 231 | print("上一次响应结果: {}".format(last_time_execute_response)) 232 | return render(request, 'case_result_diff.html', locals()) 233 | 234 | 235 | # 用例执行结果-异常信息展示 236 | @login_required 237 | def show_exception(request, execute_id): 238 | test_record = models.TestCaseExecuteResult.objects.get(id=execute_id) 239 | return render(request, 'show_exception.html', {'exception_info': test_record.exception_info}) 240 | 241 | 242 | # 用例集合执行结果 243 | @login_required 244 | def case_suite_execute_record(request): 245 | case_suite_execute_record = models.CaseSuiteExecuteRecord.objects.filter().order_by('-id') 246 | return render(request, 'case_suite_execute_record.html', 247 | {'case_suite_execute_records': get_paginator(request, case_suite_execute_record)}) 248 | 249 | 250 | # 用例集合执行结果-包含用例结果展示 251 | @login_required 252 | def suite_case_execute_record(request, suite_record_id): 253 | case_suite_execute_record = models.CaseSuiteExecuteRecord.objects.get(id=suite_record_id) 254 | suite_case_execute_records = models.CaseSuiteTestCaseExecuteRecord.objects.filter(case_suite_record=case_suite_execute_record) 255 | return render(request, 'suite_case_execute_record.html', 256 | {'suite_case_execute_records': get_paginator(request, suite_case_execute_records)}) 257 | 258 | 259 | # 用例集合执行结果-包含用例结果展示-差异比对 260 | @login_required 261 | def suite_case_result_diff(request, suite_case_record_id): 262 | suite_record_data = models.CaseSuiteTestCaseExecuteRecord.objects.get(id=suite_case_record_id) 263 | present_response = suite_record_data.response_data 264 | if present_response: 265 | present_response = json.dumps(json.loads(present_response),sort_keys=True, indent=4, ensure_ascii=False) 266 | print("当前响应: {}".format(present_response)) 267 | last_time_execute_response = suite_record_data.last_time_response_data 268 | if last_time_execute_response: 269 | last_time_execute_response = json.dumps(json.loads(last_time_execute_response), sort_keys=True, 270 | indent=4, ensure_ascii=False) 271 | print("上一次响应: {}".format(last_time_execute_response)) 272 | return render(request, 'case_result_diff.html', locals()) 273 | 274 | 275 | # 用例集合执行结果-包含用例结果展示-异常信息展示 276 | @login_required 277 | def suite_case_exception(request, suite_case_record_id): 278 | test_record = models.CaseSuiteTestCaseExecuteRecord.objects.get(id=suite_case_record_id) 279 | return render(request, 'show_exception.html', {'exception_info': test_record.exception_info}) 280 | 281 | 282 | # 用例集合执行结果单次统计 283 | def suite_case_statistics(request, suite_id): 284 | success_num = len(models.CaseSuiteTestCaseExecuteRecord.objects.filter(case_suite_record=suite_id, execute_result="成功")) 285 | fail_num = len(models.CaseSuiteTestCaseExecuteRecord.objects.filter(case_suite_record=suite_id, execute_result="失败")) 286 | suite_case_records = models.CaseSuiteTestCaseExecuteRecord.objects.filter(case_suite_record=suite_id).order_by('-id') 287 | return render(request, 'suite_case_statistics.html', 288 | {'suite_case_records': get_paginator(request, suite_case_records), 'success_num': success_num, 289 | 'fail_num': fail_num}) 290 | 291 | 292 | # 用例集合执行结果历史统计 293 | def case_suite_statistics(request, suite_id): 294 | case_suite = models.CaseSuite.objects.get(id=suite_id) 295 | success_num = len(models.CaseSuiteExecuteRecord.objects.filter(case_suite=case_suite, test_result="成功")) 296 | fail_num = len(models.CaseSuiteExecuteRecord.objects.filter(case_suite=case_suite, test_result="失败")) 297 | case_suite_records = models.CaseSuiteExecuteRecord.objects.filter(case_suite=case_suite).order_by('-id') 298 | return render(request, 'case_suite_statistics.html', 299 | {'case_suite_records': get_paginator(request, case_suite_records), 'success_num': success_num, 300 | 'fail_num': fail_num}) 301 | 302 | 303 | # 模块测试结果统计 304 | @login_required 305 | def module_statistics(request, module_id): 306 | test_module = models.Module.objects.get(id=int(module_id)) 307 | test_cases = models.TestCase.objects.filter(belong_module=test_module) 308 | test_suit_success_num = len( 309 | models.CaseSuiteTestCaseExecuteRecord.objects.filter(test_case__in=test_cases, execute_result="成功")) 310 | test_suit_fail_num = len( 311 | models.CaseSuiteTestCaseExecuteRecord.objects.filter(test_case__in=test_cases, execute_result="失败")) 312 | test_case_success_num = len( 313 | models.TestCaseExecuteResult.objects.filter(belong_test_case__in=test_cases, execute_result="成功")) 314 | test_case_fail_num = len( 315 | models.TestCaseExecuteResult.objects.filter(belong_test_case__in=test_cases, execute_result="失败")) 316 | success_num = test_suit_success_num + test_case_success_num 317 | fail_num = test_suit_fail_num + test_case_fail_num 318 | return render(request, 'module_statistics.html', 319 | {'test_module': test_module, 'success_num': success_num, 'fail_num': fail_num}) 320 | 321 | 322 | # 项目测试结果统计 323 | @login_required 324 | def project_statistics(request, project_id): 325 | test_project = models.Project.objects.get(id=int(project_id)) 326 | test_cases = models.TestCase.objects.filter(belong_project=test_project) 327 | test_suit_success_num = len( 328 | models.CaseSuiteTestCaseExecuteRecord.objects.filter(test_case__in=test_cases, execute_result="成功")) 329 | test_suit_fail_num = len( 330 | models.CaseSuiteTestCaseExecuteRecord.objects.filter(test_case__in=test_cases, execute_result="失败")) 331 | test_case_success_num = len( 332 | models.TestCaseExecuteResult.objects.filter(belong_test_case__in=test_cases, execute_result="成功")) 333 | test_case_fail_num = len( 334 | models.TestCaseExecuteResult.objects.filter(belong_test_case__in=test_cases, execute_result="失败")) 335 | success_num = test_suit_success_num + test_case_success_num 336 | fail_num = test_suit_fail_num + test_case_fail_num 337 | return render(request, 'project_statistics.html', 338 | {'test_project': test_project, 'success_num': success_num, 'fail_num': fail_num}) 339 | 340 | 341 | # 默认页的视图函数 342 | @login_required 343 | def index(request): 344 | return render(request, 'index.html') 345 | 346 | 347 | # 登录页的视图函数 348 | def login(request): 349 | print("request.session.items(): {}".format(request.session.items())) # 打印session信息 350 | if request.session.get('is_login', None): 351 | return redirect('/') 352 | # 如果是表单提交行为,则进行登录校验 353 | if request.method == "POST": 354 | login_form = UserForm(request.POST) 355 | message = "请检查填写的内容!" 356 | if login_form.is_valid(): 357 | username = login_form.cleaned_data['username'] 358 | password = login_form.cleaned_data['password'] 359 | try: 360 | # 使用django提供的身份验证功能 361 | user = auth.authenticate(username=username, password=password) # 从auth_user表中匹配信息,匹配到则返回用户对象 362 | if user is not None: 363 | print("用户【%s】登录成功" % username) 364 | auth.login(request, user) 365 | request.session['is_login'] = True 366 | # 登录成功,跳转主页 367 | return redirect('/') 368 | else: 369 | message = "用户名不存在或者密码不正确!" 370 | except: 371 | traceback.print_exc() 372 | message = "登录程序出现异常" 373 | # 用户名或密码为空,返回登录页和错误提示信息 374 | else: 375 | return render(request, 'login.html', locals()) 376 | # 不是表单提交,代表只是访问登录页 377 | else: 378 | login_form = UserForm() 379 | return render(request, 'login.html', locals()) 380 | 381 | 382 | # 注册页的视图函数 383 | def register(request): 384 | return render(request, 'register.html') 385 | 386 | 387 | # 登出的视图函数:重定向至login视图函数 388 | @login_required 389 | def logout(request): 390 | auth.logout(request) 391 | request.session.flush() 392 | return redirect("/login/") 393 | -------------------------------------------------------------------------------- /InterfaceAutoTest/static/bootstrap/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} 6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */ -------------------------------------------------------------------------------- /InterfaceAutoTest/static/bootstrap/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} -------------------------------------------------------------------------------- /InterfaceAutoTest/static/bootstrap/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | .btn-default, 7 | .btn-primary, 8 | .btn-success, 9 | .btn-info, 10 | .btn-warning, 11 | .btn-danger { 12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | } 16 | .btn-default:active, 17 | .btn-primary:active, 18 | .btn-success:active, 19 | .btn-info:active, 20 | .btn-warning:active, 21 | .btn-danger:active, 22 | .btn-default.active, 23 | .btn-primary.active, 24 | .btn-success.active, 25 | .btn-info.active, 26 | .btn-warning.active, 27 | .btn-danger.active { 28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | } 31 | .btn-default.disabled, 32 | .btn-primary.disabled, 33 | .btn-success.disabled, 34 | .btn-info.disabled, 35 | .btn-warning.disabled, 36 | .btn-danger.disabled, 37 | .btn-default[disabled], 38 | .btn-primary[disabled], 39 | .btn-success[disabled], 40 | .btn-info[disabled], 41 | .btn-warning[disabled], 42 | .btn-danger[disabled], 43 | fieldset[disabled] .btn-default, 44 | fieldset[disabled] .btn-primary, 45 | fieldset[disabled] .btn-success, 46 | fieldset[disabled] .btn-info, 47 | fieldset[disabled] .btn-warning, 48 | fieldset[disabled] .btn-danger { 49 | -webkit-box-shadow: none; 50 | box-shadow: none; 51 | } 52 | .btn-default .badge, 53 | .btn-primary .badge, 54 | .btn-success .badge, 55 | .btn-info .badge, 56 | .btn-warning .badge, 57 | .btn-danger .badge { 58 | text-shadow: none; 59 | } 60 | .btn:active, 61 | .btn.active { 62 | background-image: none; 63 | } 64 | .btn-default { 65 | text-shadow: 0 1px 0 #fff; 66 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 67 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 68 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 69 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 70 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 71 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 72 | background-repeat: repeat-x; 73 | border-color: #dbdbdb; 74 | border-color: #ccc; 75 | } 76 | .btn-default:hover, 77 | .btn-default:focus { 78 | background-color: #e0e0e0; 79 | background-position: 0 -15px; 80 | } 81 | .btn-default:active, 82 | .btn-default.active { 83 | background-color: #e0e0e0; 84 | border-color: #dbdbdb; 85 | } 86 | .btn-default.disabled, 87 | .btn-default[disabled], 88 | fieldset[disabled] .btn-default, 89 | .btn-default.disabled:hover, 90 | .btn-default[disabled]:hover, 91 | fieldset[disabled] .btn-default:hover, 92 | .btn-default.disabled:focus, 93 | .btn-default[disabled]:focus, 94 | fieldset[disabled] .btn-default:focus, 95 | .btn-default.disabled.focus, 96 | .btn-default[disabled].focus, 97 | fieldset[disabled] .btn-default.focus, 98 | .btn-default.disabled:active, 99 | .btn-default[disabled]:active, 100 | fieldset[disabled] .btn-default:active, 101 | .btn-default.disabled.active, 102 | .btn-default[disabled].active, 103 | fieldset[disabled] .btn-default.active { 104 | background-color: #e0e0e0; 105 | background-image: none; 106 | } 107 | .btn-primary { 108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 114 | background-repeat: repeat-x; 115 | border-color: #245580; 116 | } 117 | .btn-primary:hover, 118 | .btn-primary:focus { 119 | background-color: #265a88; 120 | background-position: 0 -15px; 121 | } 122 | .btn-primary:active, 123 | .btn-primary.active { 124 | background-color: #265a88; 125 | border-color: #245580; 126 | } 127 | .btn-primary.disabled, 128 | .btn-primary[disabled], 129 | fieldset[disabled] .btn-primary, 130 | .btn-primary.disabled:hover, 131 | .btn-primary[disabled]:hover, 132 | fieldset[disabled] .btn-primary:hover, 133 | .btn-primary.disabled:focus, 134 | .btn-primary[disabled]:focus, 135 | fieldset[disabled] .btn-primary:focus, 136 | .btn-primary.disabled.focus, 137 | .btn-primary[disabled].focus, 138 | fieldset[disabled] .btn-primary.focus, 139 | .btn-primary.disabled:active, 140 | .btn-primary[disabled]:active, 141 | fieldset[disabled] .btn-primary:active, 142 | .btn-primary.disabled.active, 143 | .btn-primary[disabled].active, 144 | fieldset[disabled] .btn-primary.active { 145 | background-color: #265a88; 146 | background-image: none; 147 | } 148 | .btn-success { 149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 155 | background-repeat: repeat-x; 156 | border-color: #3e8f3e; 157 | } 158 | .btn-success:hover, 159 | .btn-success:focus { 160 | background-color: #419641; 161 | background-position: 0 -15px; 162 | } 163 | .btn-success:active, 164 | .btn-success.active { 165 | background-color: #419641; 166 | border-color: #3e8f3e; 167 | } 168 | .btn-success.disabled, 169 | .btn-success[disabled], 170 | fieldset[disabled] .btn-success, 171 | .btn-success.disabled:hover, 172 | .btn-success[disabled]:hover, 173 | fieldset[disabled] .btn-success:hover, 174 | .btn-success.disabled:focus, 175 | .btn-success[disabled]:focus, 176 | fieldset[disabled] .btn-success:focus, 177 | .btn-success.disabled.focus, 178 | .btn-success[disabled].focus, 179 | fieldset[disabled] .btn-success.focus, 180 | .btn-success.disabled:active, 181 | .btn-success[disabled]:active, 182 | fieldset[disabled] .btn-success:active, 183 | .btn-success.disabled.active, 184 | .btn-success[disabled].active, 185 | fieldset[disabled] .btn-success.active { 186 | background-color: #419641; 187 | background-image: none; 188 | } 189 | .btn-info { 190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 196 | background-repeat: repeat-x; 197 | border-color: #28a4c9; 198 | } 199 | .btn-info:hover, 200 | .btn-info:focus { 201 | background-color: #2aabd2; 202 | background-position: 0 -15px; 203 | } 204 | .btn-info:active, 205 | .btn-info.active { 206 | background-color: #2aabd2; 207 | border-color: #28a4c9; 208 | } 209 | .btn-info.disabled, 210 | .btn-info[disabled], 211 | fieldset[disabled] .btn-info, 212 | .btn-info.disabled:hover, 213 | .btn-info[disabled]:hover, 214 | fieldset[disabled] .btn-info:hover, 215 | .btn-info.disabled:focus, 216 | .btn-info[disabled]:focus, 217 | fieldset[disabled] .btn-info:focus, 218 | .btn-info.disabled.focus, 219 | .btn-info[disabled].focus, 220 | fieldset[disabled] .btn-info.focus, 221 | .btn-info.disabled:active, 222 | .btn-info[disabled]:active, 223 | fieldset[disabled] .btn-info:active, 224 | .btn-info.disabled.active, 225 | .btn-info[disabled].active, 226 | fieldset[disabled] .btn-info.active { 227 | background-color: #2aabd2; 228 | background-image: none; 229 | } 230 | .btn-warning { 231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 237 | background-repeat: repeat-x; 238 | border-color: #e38d13; 239 | } 240 | .btn-warning:hover, 241 | .btn-warning:focus { 242 | background-color: #eb9316; 243 | background-position: 0 -15px; 244 | } 245 | .btn-warning:active, 246 | .btn-warning.active { 247 | background-color: #eb9316; 248 | border-color: #e38d13; 249 | } 250 | .btn-warning.disabled, 251 | .btn-warning[disabled], 252 | fieldset[disabled] .btn-warning, 253 | .btn-warning.disabled:hover, 254 | .btn-warning[disabled]:hover, 255 | fieldset[disabled] .btn-warning:hover, 256 | .btn-warning.disabled:focus, 257 | .btn-warning[disabled]:focus, 258 | fieldset[disabled] .btn-warning:focus, 259 | .btn-warning.disabled.focus, 260 | .btn-warning[disabled].focus, 261 | fieldset[disabled] .btn-warning.focus, 262 | .btn-warning.disabled:active, 263 | .btn-warning[disabled]:active, 264 | fieldset[disabled] .btn-warning:active, 265 | .btn-warning.disabled.active, 266 | .btn-warning[disabled].active, 267 | fieldset[disabled] .btn-warning.active { 268 | background-color: #eb9316; 269 | background-image: none; 270 | } 271 | .btn-danger { 272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 278 | background-repeat: repeat-x; 279 | border-color: #b92c28; 280 | } 281 | .btn-danger:hover, 282 | .btn-danger:focus { 283 | background-color: #c12e2a; 284 | background-position: 0 -15px; 285 | } 286 | .btn-danger:active, 287 | .btn-danger.active { 288 | background-color: #c12e2a; 289 | border-color: #b92c28; 290 | } 291 | .btn-danger.disabled, 292 | .btn-danger[disabled], 293 | fieldset[disabled] .btn-danger, 294 | .btn-danger.disabled:hover, 295 | .btn-danger[disabled]:hover, 296 | fieldset[disabled] .btn-danger:hover, 297 | .btn-danger.disabled:focus, 298 | .btn-danger[disabled]:focus, 299 | fieldset[disabled] .btn-danger:focus, 300 | .btn-danger.disabled.focus, 301 | .btn-danger[disabled].focus, 302 | fieldset[disabled] .btn-danger.focus, 303 | .btn-danger.disabled:active, 304 | .btn-danger[disabled]:active, 305 | fieldset[disabled] .btn-danger:active, 306 | .btn-danger.disabled.active, 307 | .btn-danger[disabled].active, 308 | fieldset[disabled] .btn-danger.active { 309 | background-color: #c12e2a; 310 | background-image: none; 311 | } 312 | .thumbnail, 313 | .img-thumbnail { 314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 315 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 316 | } 317 | .dropdown-menu > li > a:hover, 318 | .dropdown-menu > li > a:focus { 319 | background-color: #e8e8e8; 320 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 321 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 322 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 323 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 325 | background-repeat: repeat-x; 326 | } 327 | .dropdown-menu > .active > a, 328 | .dropdown-menu > .active > a:hover, 329 | .dropdown-menu > .active > a:focus { 330 | background-color: #2e6da4; 331 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 332 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 333 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 334 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 336 | background-repeat: repeat-x; 337 | } 338 | .navbar-default { 339 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 340 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 342 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 344 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 345 | background-repeat: repeat-x; 346 | border-radius: 4px; 347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 349 | } 350 | .navbar-default .navbar-nav > .open > a, 351 | .navbar-default .navbar-nav > .active > a { 352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 357 | background-repeat: repeat-x; 358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 360 | } 361 | .navbar-brand, 362 | .navbar-nav > li > a { 363 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 364 | } 365 | .navbar-inverse { 366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 371 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 372 | background-repeat: repeat-x; 373 | border-radius: 4px; 374 | } 375 | .navbar-inverse .navbar-nav > .open > a, 376 | .navbar-inverse .navbar-nav > .active > a { 377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 382 | background-repeat: repeat-x; 383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 385 | } 386 | .navbar-inverse .navbar-brand, 387 | .navbar-inverse .navbar-nav > li > a { 388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 389 | } 390 | .navbar-static-top, 391 | .navbar-fixed-top, 392 | .navbar-fixed-bottom { 393 | border-radius: 0; 394 | } 395 | @media (max-width: 767px) { 396 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 399 | color: #fff; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | } 408 | .alert { 409 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 412 | } 413 | .alert-success { 414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 419 | background-repeat: repeat-x; 420 | border-color: #b2dba1; 421 | } 422 | .alert-info { 423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 428 | background-repeat: repeat-x; 429 | border-color: #9acfea; 430 | } 431 | .alert-warning { 432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #f5e79e; 439 | } 440 | .alert-danger { 441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 446 | background-repeat: repeat-x; 447 | border-color: #dca7a7; 448 | } 449 | .progress { 450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .progress-bar { 458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .progress-bar-success { 466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 471 | background-repeat: repeat-x; 472 | } 473 | .progress-bar-info { 474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 479 | background-repeat: repeat-x; 480 | } 481 | .progress-bar-warning { 482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 487 | background-repeat: repeat-x; 488 | } 489 | .progress-bar-danger { 490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 495 | background-repeat: repeat-x; 496 | } 497 | .progress-bar-striped { 498 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 499 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 500 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 501 | } 502 | .list-group { 503 | border-radius: 4px; 504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 505 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 506 | } 507 | .list-group-item.active, 508 | .list-group-item.active:hover, 509 | .list-group-item.active:focus { 510 | text-shadow: 0 -1px 0 #286090; 511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 516 | background-repeat: repeat-x; 517 | border-color: #2b669a; 518 | } 519 | .list-group-item.active .badge, 520 | .list-group-item.active:hover .badge, 521 | .list-group-item.active:focus .badge { 522 | text-shadow: none; 523 | } 524 | .panel { 525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 526 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 527 | } 528 | .panel-default > .panel-heading { 529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 534 | background-repeat: repeat-x; 535 | } 536 | .panel-primary > .panel-heading { 537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 542 | background-repeat: repeat-x; 543 | } 544 | .panel-success > .panel-heading { 545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 550 | background-repeat: repeat-x; 551 | } 552 | .panel-info > .panel-heading { 553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 558 | background-repeat: repeat-x; 559 | } 560 | .panel-warning > .panel-heading { 561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 566 | background-repeat: repeat-x; 567 | } 568 | .panel-danger > .panel-heading { 569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 574 | background-repeat: repeat-x; 575 | } 576 | .well { 577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 582 | background-repeat: repeat-x; 583 | border-color: #dcdcdc; 584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 586 | } 587 | /*# sourceMappingURL=bootstrap-theme.css.map */ 588 | --------------------------------------------------------------------------------