mirror of
https://gitee.com/mao-peng/MangoTestingPlatform.git
synced 2025-12-06 11:59:15 +08:00
Merge branch 'refs/heads/master' into dev
# Conflicts: # MangoServer/src/auto_test/auto_ui/views/ui_case.py # MangoServer/src/auto_test/auto_user/views/user.py # MangoServer/src/settings/master.py # MangoServer/src/settings/prod.py # MangoServer/src/settings/test.py # mango-console/.env.test # mango-console/src/views/config/test-files/index.vue # mango-console/src/views/uitest/page/elements/index.vue
This commit is contained in:
@@ -1,23 +1,18 @@
|
||||
FROM python:3.10.16-slim AS builder
|
||||
WORKDIR /app
|
||||
RUN echo "deb http://mirrors.aliyun.com/debian bookworm main non-free non-free-firmware" > /etc/apt/sources.list && \
|
||||
echo "deb http://mirrors.aliyun.com/debian bookworm-updates main non-free non-free-firmware" >> /etc/apt/sources.list && \
|
||||
echo "deb http://mirrors.aliyun.com/debian bookworm-backports main non-free non-free-firmware" >> /etc/apt/sources.list && \
|
||||
echo "deb http://mirrors.aliyun.com/debian-security bookworm-security main non-free non-free-firmware" >> /etc/apt/sources.list
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
python3-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir --user -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
|
||||
COPY . .
|
||||
|
||||
FROM python:3.10.16-slim
|
||||
WORKDIR /app
|
||||
RUN echo "deb http://mirrors.aliyun.com/debian bookworm main non-free non-free-firmware" > /etc/apt/sources.list && \
|
||||
echo "deb http://mirrors.aliyun.com/debian bookworm-updates main non-free non-free-firmware" >> /etc/apt/sources.list && \
|
||||
echo "deb http://mirrors.aliyun.com/debian bookworm-backports main non-free non-free-firmware" >> /etc/apt/sources.list && \
|
||||
echo "deb http://mirrors.aliyun.com/debian-security bookworm-security main non-free non-free-firmware" >> /etc/apt/sources.list
|
||||
RUN echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list && \
|
||||
echo "deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
|
||||
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
|
||||
echo "deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
|
||||
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
|
||||
echo "deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@@ -4,7 +4,7 @@ import atexit
|
||||
import time
|
||||
from django.apps import AppConfig
|
||||
|
||||
from src.auto_test.auto_api.service.test_case.case_flow import CaseFlow
|
||||
from src.auto_test.auto_api.service.test_case.case_flow import ApiCaseFlow
|
||||
|
||||
|
||||
class AutoApiConfig(AppConfig):
|
||||
@@ -21,7 +21,7 @@ class AutoApiConfig(AppConfig):
|
||||
atexit.register(self.shutdown)
|
||||
|
||||
def test_case_consumption(self):
|
||||
self.case_flow = CaseFlow()
|
||||
self.case_flow = ApiCaseFlow()
|
||||
self.api_task = Thread(target=self.case_flow.process_tasks)
|
||||
self.api_task.daemon = True
|
||||
self.api_task.start()
|
||||
|
||||
@@ -10,37 +10,36 @@ from queue import Queue
|
||||
import time
|
||||
|
||||
from mangokit import Mango
|
||||
from mangokit import singleton
|
||||
from src.models.system_model import ConsumerCaseModel
|
||||
from src.settings import IS_SEND_MAIL
|
||||
from src.tools.log_collector import log
|
||||
|
||||
|
||||
@singleton
|
||||
class CaseFlow:
|
||||
class ApiCaseFlow:
|
||||
queue = Queue()
|
||||
max_tasks = 2
|
||||
|
||||
def __init__(self):
|
||||
self.executor = ThreadPoolExecutor(max_workers=self.max_tasks)
|
||||
self.running = True
|
||||
executor = ThreadPoolExecutor(max_workers=max_tasks)
|
||||
running = True
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
@classmethod
|
||||
def stop(cls):
|
||||
cls.running = False
|
||||
|
||||
def process_tasks(self):
|
||||
@classmethod
|
||||
def process_tasks(cls):
|
||||
case_model = None
|
||||
while self.running:
|
||||
while cls.running:
|
||||
try:
|
||||
if not self.queue.empty():
|
||||
case_model = self.queue.get()
|
||||
self.executor.submit(self.execute_task, case_model)
|
||||
if not cls.queue.empty():
|
||||
case_model = cls.queue.get()
|
||||
cls.executor.submit(cls.execute_task, case_model)
|
||||
time.sleep(0.1)
|
||||
except Exception as error:
|
||||
trace = traceback.format_exc()
|
||||
log.system.error(f'API线程池发生异常:{error},报错:{trace}')
|
||||
if IS_SEND_MAIL:
|
||||
Mango.s(self.process_tasks, error, trace, case_model=case_model)
|
||||
Mango.s(cls.process_tasks, error, trace, case_model=case_model)
|
||||
|
||||
@classmethod
|
||||
def execute_task(cls, case_model: ConsumerCaseModel):
|
||||
|
||||
@@ -17,7 +17,7 @@ from src.auto_test.auto_system.service.tasks.add_tasks import AddTasks
|
||||
from src.auto_test.auto_system.views.product_module import ProductModuleSerializers
|
||||
from src.auto_test.auto_system.views.project_product import ProjectProductSerializersC
|
||||
from src.auto_test.auto_user.views.user import UserSerializers
|
||||
from src.enums.tools_enum import StatusEnum, AutoTestTypeEnum
|
||||
from src.enums.tools_enum import StatusEnum, TestCaseTypeEnum
|
||||
from src.models.api_model import ApiCaseResultModel
|
||||
from src.tools.decorator.error_response import error_response
|
||||
from src.tools.log_collector import log
|
||||
@@ -100,9 +100,9 @@ class ApiCaseViews(ViewSet):
|
||||
test_env=request.data.get('test_env'),
|
||||
is_notice=StatusEnum.FAIL.value,
|
||||
user_id=request.user['id'],
|
||||
_type=AutoTestTypeEnum.API.value,
|
||||
)
|
||||
add_tasks.add_test_suite_details(case_id_list)
|
||||
for case_id in case_id_list:
|
||||
add_tasks.add_test_suite_details(case_id, TestCaseTypeEnum.API)
|
||||
return ResponseData.success(RESPONSE_MSG_0111)
|
||||
|
||||
@action(methods=['get'], detail=False)
|
||||
|
||||
@@ -10,36 +10,34 @@ from queue import Queue
|
||||
import time
|
||||
|
||||
from mangokit import Mango
|
||||
from mangokit import singleton
|
||||
from src.models.system_model import ConsumerCaseModel
|
||||
from src.settings import IS_SEND_MAIL
|
||||
from src.tools.log_collector import log
|
||||
|
||||
|
||||
@singleton
|
||||
class CaseFlow:
|
||||
class PytestCaseFlow:
|
||||
queue = Queue()
|
||||
max_tasks = 2
|
||||
executor = ThreadPoolExecutor(max_workers=max_tasks)
|
||||
running = True
|
||||
|
||||
def __init__(self):
|
||||
self.executor = ThreadPoolExecutor(max_workers=self.max_tasks)
|
||||
self.running = True
|
||||
@classmethod
|
||||
def stop(cls):
|
||||
cls.running = False
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
|
||||
def process_tasks(self):
|
||||
while self.running:
|
||||
@classmethod
|
||||
def process_tasks(cls):
|
||||
while cls.running:
|
||||
try:
|
||||
if not self.queue.empty():
|
||||
case_model = self.queue.get()
|
||||
self.executor.submit(self.execute_task, case_model)
|
||||
if not cls.queue.empty():
|
||||
case_model = cls.queue.get()
|
||||
cls.executor.submit(cls.execute_task, case_model)
|
||||
time.sleep(0.1)
|
||||
except Exception as error:
|
||||
trace = traceback.format_exc()
|
||||
log.system.error(f'Pytest线程池发生异常:{error},报错:{trace}')
|
||||
if IS_SEND_MAIL:
|
||||
Mango.s(self.process_tasks, error, trace)
|
||||
Mango.s(cls.process_tasks, error, trace)
|
||||
|
||||
@classmethod
|
||||
def execute_task(cls, case_model: ConsumerCaseModel):
|
||||
|
||||
@@ -111,21 +111,19 @@ class ChatConsumer(WebsocketConsumer):
|
||||
pass
|
||||
else:
|
||||
obj.send(send_data.model_dump_json())
|
||||
if DEBUG:
|
||||
log.system.info(
|
||||
f'发送的用户:{send_data.user}\n'
|
||||
f'发送的客户端类型:{ClientTypeEnum.get_value(1)}\n'
|
||||
f'发送的数据:{send_data.model_dump_json() if send_data.data else None}'
|
||||
)
|
||||
log.system.warning(
|
||||
f'发送的用户:{send_data.user}'
|
||||
f'发送的客户端类型:{ClientTypeEnum.get_value(1)}'
|
||||
f'发送的数据:{send_data.model_dump_json() if send_data.data else None}'
|
||||
)
|
||||
elif send_data.is_notice == ClientTypeEnum.ACTUATOR.value:
|
||||
obj = SocketUser.get_user_client_obj(send_data.user)
|
||||
obj.send(send_data.model_dump_json())
|
||||
if DEBUG:
|
||||
log.system.info(
|
||||
f'发送的用户:{send_data.user}\n'
|
||||
f'发送的客户端类型:{ClientTypeEnum.get_value(2)}\n'
|
||||
f'发送的数据:{send_data.model_dump_json() if send_data.data else None}'
|
||||
)
|
||||
log.system.warning(
|
||||
f'发送的用户:{send_data.user}'
|
||||
f'发送的客户端类型:{ClientTypeEnum.get_value(2)}'
|
||||
f'发送的数据:{send_data.model_dump_json() if send_data.data else None}'
|
||||
)
|
||||
|
||||
def inside_send(self,
|
||||
msg: str,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.1.5 on 2025-02-28 10:14
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auto_system', '0006_remove_projectproduct_auto_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='testsuite',
|
||||
name='tasks',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='auto_system.tasks'),
|
||||
),
|
||||
]
|
||||
@@ -253,7 +253,7 @@ class TestSuite(models.Model):
|
||||
project_product = models.ForeignKey(to=ProjectProduct, to_field="id", on_delete=models.PROTECT)
|
||||
test_env = models.SmallIntegerField(verbose_name="测试环境")
|
||||
user = models.ForeignKey(to=User, to_field="id", verbose_name='用例执行人', on_delete=models.PROTECT)
|
||||
tasks = models.ForeignKey(to=Tasks, to_field="id", on_delete=models.PROTECT)
|
||||
tasks = models.ForeignKey(to=Tasks, to_field="id",on_delete=models.SET_NULL, null=True)
|
||||
|
||||
status = models.SmallIntegerField(verbose_name="测试结果")
|
||||
is_notice = models.SmallIntegerField(verbose_name="是否发送通知")
|
||||
|
||||
@@ -12,9 +12,11 @@ from django.db.utils import Error
|
||||
from django.utils import timezone
|
||||
|
||||
from mangokit import Mango
|
||||
from src.auto_test.auto_api.service.test_case.case_flow import CaseFlow
|
||||
from src.auto_test.auto_api.service.test_case.case_flow import ApiCaseFlow
|
||||
from src.auto_test.auto_pytest.service.test_case.case_flow import PytestCaseFlow
|
||||
from src.auto_test.auto_system.models import TestSuiteDetails, TestSuite
|
||||
from src.enums.tools_enum import TaskEnum, AutoTestTypeEnum
|
||||
from src.auto_test.auto_ui.service.test_case.case_flow import UiCaseFlow
|
||||
from src.enums.tools_enum import TaskEnum, TestCaseTypeEnum
|
||||
from src.exceptions import MangoServerError
|
||||
from src.models.system_model import ConsumerCaseModel
|
||||
from src.settings import IS_SEND_MAIL
|
||||
@@ -42,7 +44,7 @@ class ConsumerThread:
|
||||
).first()
|
||||
if test_suite_details:
|
||||
test_suite = TestSuite.objects.get(id=test_suite_details.test_suite.id)
|
||||
api_case_model = ConsumerCaseModel(
|
||||
case_model = ConsumerCaseModel(
|
||||
test_suite_details=test_suite_details.id,
|
||||
test_suite=test_suite_details.test_suite.id,
|
||||
case_id=test_suite_details.case_id,
|
||||
@@ -50,15 +52,8 @@ class ConsumerThread:
|
||||
user_id=test_suite.user.id,
|
||||
tasks_id=test_suite.tasks.id if test_suite.tasks else None,
|
||||
)
|
||||
if test_suite_details.type == AutoTestTypeEnum.UI.value:
|
||||
self.ui(0, test_suite, test_suite_details)
|
||||
elif test_suite_details.type == AutoTestTypeEnum.API.value:
|
||||
self.api(api_case_model)
|
||||
else:
|
||||
self.mango_pytest(test_suite, test_suite_details)
|
||||
test_suite_details.retry += 1
|
||||
test_suite_details.push_time = timezone.now()
|
||||
test_suite_details.save()
|
||||
self.send_case(test_suite, test_suite_details, case_model)
|
||||
self.update_status_proceed(test_suite, test_suite_details)
|
||||
|
||||
if time.time() - reset_tims > self.clean_time * 60:
|
||||
reset_tims = time.time()
|
||||
@@ -74,48 +69,25 @@ class ConsumerThread:
|
||||
if IS_SEND_MAIL:
|
||||
Mango.s(self.consumer, error, trace, )
|
||||
|
||||
def ui(self, environment_error, test_suite, test_suite_details):
|
||||
def send_case(self, test_suite, test_suite_details, case_model: ConsumerCaseModel, retry=0, max_retry=3):
|
||||
retry += 1
|
||||
try:
|
||||
if test_suite_details.type == TestCaseTypeEnum.UI.value:
|
||||
UiCaseFlow.add_task(case_model)
|
||||
elif test_suite_details.type == TestCaseTypeEnum.API.value:
|
||||
ApiCaseFlow.add_task(case_model)
|
||||
else:
|
||||
PytestCaseFlow.add_task(case_model)
|
||||
log.system.info(
|
||||
f'推送UI任务成功,数据:{{"case_id":{test_suite_details.case_id},"test_suite":{test_suite_details.test_suite.id},"test_suite_details":{test_suite_details.id}}}')
|
||||
self.update_status_proceed(test_suite, test_suite_details)
|
||||
f'推送{TestCaseTypeEnum.get_value(test_suite_details.type)}任务成功,test_suite_details":{test_suite_details.id}')
|
||||
except MangoServerError as error:
|
||||
log.system.warning(f'UI测试任务发生已知错误,忽略错误,等待重新开始:{error.msg}')
|
||||
except Exception as error:
|
||||
self.consumer_error(test_suite, test_suite_details, error, traceback.format_exc())
|
||||
|
||||
def api(self, case_model: ConsumerCaseModel):
|
||||
try:
|
||||
CaseFlow().add_task(case_model)
|
||||
log.system.info(
|
||||
f'推送API任务成功,数据:{{"case_id":{test_suite_details.case_id},"test_suite":{test_suite_details.test_suite.id},"test_suite_details":{test_suite_details.id}}}')
|
||||
self.update_status_proceed(test_suite, test_suite_details)
|
||||
except MangoServerError as error:
|
||||
log.system.warning(f'API测试任务发生已知错误,忽略错误,等待重新开始:{error.msg}')
|
||||
except Exception as error:
|
||||
self.consumer_error(test_suite, test_suite_details, error, traceback.format_exc())
|
||||
|
||||
def mango_pytest(self, case_model: ConsumerCaseModel):
|
||||
try:
|
||||
send_case = CmdTest(
|
||||
user_id=test_suite.user.id,
|
||||
username=test_suite.user.username,
|
||||
test_env=test_suite_details.test_env,
|
||||
tasks_id=test_suite.tasks.id,
|
||||
is_notice=test_suite.is_notice,
|
||||
is_send=True
|
||||
)
|
||||
send_case.test_case(
|
||||
test_suite=test_suite_details.test_suite.id,
|
||||
test_suite_details=test_suite_details.id
|
||||
)
|
||||
log.system.info(
|
||||
f'推送UI任务成功,数据:{{"case_id":{test_suite_details.case_id},"test_suite":{test_suite_details.test_suite.id},"test_suite_details":{test_suite_details.id}}}')
|
||||
self.update_status_proceed(test_suite, test_suite_details)
|
||||
except MangoServerError as error:
|
||||
log.system.warning(f'UI测试任务发生已知错误,忽略错误,等待重新开始:{error.msg}')
|
||||
except Exception as error:
|
||||
self.consumer_error(test_suite, test_suite_details, error, traceback.format_exc())
|
||||
if retry > max_retry:
|
||||
self.consumer_error(test_suite, test_suite_details, error, traceback.format_exc())
|
||||
return
|
||||
else:
|
||||
self.send_case(test_suite, test_suite_details, case_model, retry, max_retry)
|
||||
|
||||
def clean_proceed(self):
|
||||
"""
|
||||
@@ -148,12 +120,17 @@ class ConsumerThread:
|
||||
def update_status_proceed(self, test_suite, test_suite_details):
|
||||
test_suite.status = TaskEnum.PROCEED.value
|
||||
test_suite.save()
|
||||
|
||||
test_suite_details.status = TaskEnum.PROCEED.value
|
||||
test_suite_details.retry += 1
|
||||
test_suite_details.push_time = timezone.now()
|
||||
test_suite_details.save()
|
||||
|
||||
def consumer_error(self, test_suite, test_suite_details, error, trace):
|
||||
test_suite.status = TaskEnum.FAIL.value
|
||||
test_suite.save()
|
||||
test_suite_details.status = TaskEnum.FAIL.value
|
||||
test_suite_details.error_message = f'发生未知异常,请联系管理员处理,类型:{AutoTestTypeEnum.get_value(test_suite.type)},异常内容:{error}'
|
||||
test_suite_details.error_message = f'测试{TestCaseTypeEnum.get_value(test_suite_details.type)}类型:,异常类型:{error},报错内容:{trace}'
|
||||
test_suite.save()
|
||||
if IS_SEND_MAIL:
|
||||
Mango.s(self.consumer_error, error, trace)
|
||||
|
||||
@@ -3,51 +3,23 @@
|
||||
# @Description:
|
||||
# @Time : 2024-11-23 20:38
|
||||
# @Author : 毛鹏
|
||||
import traceback
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from queue import Queue
|
||||
|
||||
import time
|
||||
|
||||
from mangokit import Mango
|
||||
from mangokit import singleton
|
||||
from src.auto_test.auto_system.models import TestSuite, TestSuiteDetails
|
||||
from src.auto_test.auto_system.service.socket_link.socket_user import SocketUser
|
||||
from src.auto_test.auto_ui.service.test_case.test_case import TestCase
|
||||
from src.enums.tools_enum import TaskEnum
|
||||
from src.models.system_model import ConsumerCaseModel
|
||||
from src.settings import IS_SEND_MAIL
|
||||
from src.tools.log_collector import log
|
||||
|
||||
|
||||
@singleton
|
||||
class CaseFlow:
|
||||
queue = Queue()
|
||||
max_tasks = 2
|
||||
class UiCaseFlow:
|
||||
current_index = 0
|
||||
|
||||
def __init__(self):
|
||||
self.executor = ThreadPoolExecutor(max_workers=self.max_tasks)
|
||||
self.running = True
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
|
||||
def process_tasks(self):
|
||||
while self.running:
|
||||
try:
|
||||
if not self.queue.empty():
|
||||
case_model: ConsumerCaseModel = self.queue.get()
|
||||
self.executor.submit(self.execute_task, case_model, 0, 3)
|
||||
time.sleep(0.2)
|
||||
except Exception as error:
|
||||
trace = traceback.format_exc()
|
||||
log.ui.error(f'UI线程池发生异常:{error},报错:{trace}')
|
||||
if IS_SEND_MAIL:
|
||||
Mango.s(self.process_tasks, error, trace)
|
||||
|
||||
@classmethod
|
||||
def execute_task(cls, case_model: ConsumerCaseModel, retry=0, max_retry=3):
|
||||
retry += 1
|
||||
user_list = [i for i in SocketUser.user if i.client_obj]
|
||||
if not user_list:
|
||||
log.system.warning('用户列表为空,无法发送任务,请先保持至少一个执行器是登录状态~')
|
||||
@@ -57,6 +29,7 @@ class CaseFlow:
|
||||
cls.current_index = (cls.current_index + 1) % len(user_list)
|
||||
user = user_list[cls.current_index]
|
||||
except IndexError:
|
||||
time.sleep(3)
|
||||
return cls.execute_task(case_model, retry, max_retry)
|
||||
send_case = TestCase(
|
||||
user_id=user.user_id,
|
||||
@@ -87,4 +60,4 @@ class CaseFlow:
|
||||
|
||||
@classmethod
|
||||
def add_task(cls, case_model: ConsumerCaseModel):
|
||||
cls.queue.put(case_model)
|
||||
cls.execute_task(case_model)
|
||||
|
||||
@@ -16,7 +16,7 @@ from src.auto_test.auto_ui.models import UiCase
|
||||
from src.auto_test.auto_ui.service.test_case.test_case import TestCase
|
||||
from src.auto_test.auto_user.views.user import UserSerializers
|
||||
from src.enums.system_enum import ClientNameEnum
|
||||
from src.enums.tools_enum import StatusEnum, AutoTestTypeEnum
|
||||
from src.enums.tools_enum import StatusEnum, TestCaseTypeEnum
|
||||
from src.tools.decorator.error_response import error_response
|
||||
from src.tools.view.model_crud import ModelCRUD
|
||||
from src.tools.view.response_data import ResponseData
|
||||
@@ -104,7 +104,8 @@ class UiCaseViews(ViewSet):
|
||||
is_notice=StatusEnum.FAIL.value,
|
||||
user_id=request.user['id'],
|
||||
)
|
||||
add_tasks.add_test_suite_details(case_id_list, AutoTestTypeEnum.UI.value)
|
||||
for case_id in case_id_list:
|
||||
add_tasks.add_test_suite_details(case_id, TestCaseTypeEnum.UI)
|
||||
return ResponseData.success(RESPONSE_MSG_0074, value=(ClientNameEnum.DRIVER.value,))
|
||||
|
||||
@action(methods=['POST'], detail=False)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# @Author : 毛鹏
|
||||
import os
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
|
||||
import time
|
||||
from django.forms import model_to_dict
|
||||
@@ -14,8 +15,8 @@ from rest_framework.decorators import action
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from src import settings
|
||||
from mangokit import EncryptionTool
|
||||
from src import settings
|
||||
from src.auto_test.auto_system.service.menu import ad_routes
|
||||
from src.auto_test.auto_user.models import User
|
||||
from src.auto_test.auto_user.views.role import RoleSerializers
|
||||
@@ -218,9 +219,7 @@ class LoginViews(ViewSet):
|
||||
@error_response('user')
|
||||
@action(methods=['get'], detail=False)
|
||||
def get_download(self, request: Request):
|
||||
file_name = request.query_params.get('file_name')
|
||||
|
||||
file_name = unquote(request.query_params.get('file_name'))
|
||||
file_path = os.path.join(settings.BASE_DIR, 'upload_template', file_name)
|
||||
response = FileResponse(open(file_path, 'rb'))
|
||||
response['Content-Disposition'] = f'attachment; filename="{file_name}"'
|
||||
response = FileResponse(open(file_path, 'rb'), as_attachment=True, filename=file_name)
|
||||
return response
|
||||
|
||||
@@ -12,10 +12,10 @@ IS_SQLITE = False # 是否选用sqlite作为数据源,默认使用mysql
|
||||
|
||||
# ************************ Mysql配置 ************************ #
|
||||
MYSQL_PORT = 3306
|
||||
MYSQL_DB_NAME = 'mango_server'
|
||||
MYSQL_DB_NAME = 'dev_mango_server'
|
||||
MYSQL_USER = 'root'
|
||||
MYSQL_PASSWORD = 'mP123456&'
|
||||
MYSQL_IP = '172.27.190.172'
|
||||
MYSQL_IP = '172.25.239.230'
|
||||
|
||||
# ************************ DEBUG配置 ************************ #
|
||||
# 这里也控制了是否使用minio
|
||||
|
||||
@@ -28,13 +28,13 @@ IS_DEBUG_LOG = False
|
||||
REDIS = False
|
||||
# ************************ Minio配置 ************************ #
|
||||
IS_MINIO = True
|
||||
|
||||
MINIO_STORAGE_ENDPOINT = 'minio:9000' # 访问IP+端口
|
||||
MINIO_STORAGE_ACCESS_KEY = 'Qs6vh4jyQtakTPcjpGTD' # ACCESS_KEY
|
||||
MINIO_STORAGE_SECRET_KEY = 'l9kmezbVChMieUtmCWg5MDIcRYetThnK0t8NkkTk' # SECRET_KEY
|
||||
MINIO_STORAGE_USE_HTTPS = False # 如果使用 HTTPS,设置为 True
|
||||
MINIO_STORAGE_MEDIA_BUCKET_NAME = 'mango-file' # 桶名称
|
||||
MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True # 桶不存在时自动创建
|
||||
if IS_MINIO:
|
||||
MINIO_STORAGE_ENDPOINT = 'minio:9000' # 访问IP+端口
|
||||
MINIO_STORAGE_ACCESS_KEY = 'Qs6vh4jyQtakTPcjpGTD' # ACCESS_KEY
|
||||
MINIO_STORAGE_SECRET_KEY = 'l9kmezbVChMieUtmCWg5MDIcRYetThnK0t8NkkTk' # SECRET_KEY
|
||||
MINIO_STORAGE_USE_HTTPS = False # 如果使用 HTTPS,设置为 True
|
||||
MINIO_STORAGE_MEDIA_BUCKET_NAME = 'mango-file' # 桶名称
|
||||
MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True # 桶不存在时自动创建
|
||||
|
||||
# ************************ 是否允许删除 ************************ #
|
||||
IS_DELETE = True
|
||||
|
||||
@@ -28,13 +28,13 @@ IS_DEBUG_LOG = False
|
||||
REDIS = False
|
||||
# ************************ Minio配置 ************************ #
|
||||
IS_MINIO = True
|
||||
|
||||
MINIO_STORAGE_ENDPOINT = 'minio:9000'
|
||||
MINIO_STORAGE_ACCESS_KEY = 'MCIK0dsiAFwgjthxlTdA'
|
||||
MINIO_STORAGE_SECRET_KEY = '0rx2pFV3bc5q7q0hj7c66bRlcVhozKbv5E05Tqkx'
|
||||
MINIO_STORAGE_USE_HTTPS = False # 如果使用 HTTPS,设置为 True
|
||||
MINIO_STORAGE_MEDIA_BUCKET_NAME = 'mango-file' # 桶名称
|
||||
MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True # 桶不存在时自动创建
|
||||
if IS_MINIO:
|
||||
MINIO_STORAGE_ENDPOINT = 'minio:9000'
|
||||
MINIO_STORAGE_ACCESS_KEY = 'MCIK0dsiAFwgjthxlTdA'
|
||||
MINIO_STORAGE_SECRET_KEY = '0rx2pFV3bc5q7q0hj7c66bRlcVhozKbv5E05Tqkx'
|
||||
MINIO_STORAGE_USE_HTTPS = False # 如果使用 HTTPS,设置为 True
|
||||
MINIO_STORAGE_MEDIA_BUCKET_NAME = 'mango-file' # 桶名称
|
||||
MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True # 桶不存在时自动创建
|
||||
|
||||
# ************************ 是否允许删除 ************************ #
|
||||
IS_DELETE = False
|
||||
|
||||
@@ -29,13 +29,15 @@ REDIS = False
|
||||
# ************************ Minio配置 ************************ #
|
||||
IS_MINIO = True
|
||||
|
||||
MINIO_STORAGE_ENDPOINT = 'minio:9000' # 访问IP+端口
|
||||
MINIO_STORAGE_ACCESS_KEY = 'Qs6vh4jyQtakTPcjpGTD' # ACCESS_KEY
|
||||
MINIO_STORAGE_SECRET_KEY = 'l9kmezbVChMieUtmCWg5MDIcRYetThnK0t8NkkTk' # SECRET_KEY
|
||||
MINIO_STORAGE_USE_HTTPS = False # 如果使用 HTTPS,设置为 True
|
||||
MINIO_STORAGE_MEDIA_BUCKET_NAME = 'mango-file' # 桶名称
|
||||
MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True # 桶不存在时自动创建
|
||||
|
||||
if IS_MINIO:
|
||||
MINIO_STORAGE_ENDPOINT = 'minio:9000' # 访问IP+端口
|
||||
MINIO_STORAGE_USE_HTTPS = False # 如果使用 HTTPS,设置为 True
|
||||
MINIO_STORAGE_MEDIA_BUCKET_NAME = 'mango-file' # 桶名称
|
||||
MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True # 桶不存在时自动创建
|
||||
# MINIO_STORAGE_ACCESS_KEY = 'jkPxfhklQqhUmDEpP0po' # ACCESS_KEY
|
||||
# MINIO_STORAGE_SECRET_KEY = 'aQriZMcIH8rVXc5uNpondikDNOLdPbsba77dT6mF' # SECRET_KEY
|
||||
MINIO_STORAGE_ACCESS_KEY = 'eQUpBpIGUgHc1f2nZbte' # 家里的
|
||||
MINIO_STORAGE_SECRET_KEY = 'AqNnxHTrxVAZtPUgu6lLEArekqjHfMtku4tM1qgz' # 家里的
|
||||
# ************************ 是否允许删除 ************************ #
|
||||
IS_DELETE = True
|
||||
# *************** 是否发送error日志协助芒果修复问题 *************** #
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
BUILD_PATH=/
|
||||
VITE_APP_ENV='test'
|
||||
VITE_APP_BASE_URL = 'http://172.27.182.168:8000'
|
||||
VITE_APP_SOCKET_URL = 'ws://172.27.182.168:8000/web/socket?'
|
||||
VITE_APP_MINIO_URL = 'http://172.27.182.168:9000'
|
||||
VITE_APP_BASE_URL = 'http://172.25.239.230:8000'
|
||||
VITE_APP_SOCKET_URL = 'ws://172.25.239.230:8000/web/socket?'
|
||||
VITE_APP_MINIO_URL = 'http://172.25.239.230:9000'
|
||||
VITE_IS_INDEX_WINDOW = 'true'
|
||||
@@ -14,6 +14,9 @@ export function getUserFile(type = 0) {
|
||||
export function postUserFile(data: object) {
|
||||
return post({
|
||||
url: '/system/file',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
data: () => {
|
||||
return data
|
||||
},
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
<div class="container">
|
||||
<span>测试文件</span>
|
||||
</div>
|
||||
<a-upload @before-upload="beforeUpload" :show-file-list="false" />
|
||||
<a-upload
|
||||
@before-upload="beforeUpload"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
/>
|
||||
</a-space>
|
||||
|
||||
<a-tabs />
|
||||
@@ -28,14 +32,14 @@
|
||||
:data-index="item.key"
|
||||
:fixed="item.fixed"
|
||||
>
|
||||
<template v-if="item.key === 'index'" :class="record" #cell="{ record }">
|
||||
<template v-if="item.key === 'index'" #cell="{ record }">
|
||||
{{ record.id }}
|
||||
</template>
|
||||
<template v-else-if="item.key === 'actions'" #cell="{ record }">
|
||||
<a-button type="text" size="mini" @click="onDownload(record)">下载 </a-button>
|
||||
<a-button type="text" size="mini" @click="onDownload(record)">下载</a-button>
|
||||
<a-button status="danger" type="text" size="mini" @click="onDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
>删除
|
||||
</a-button>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
@@ -47,16 +51,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { usePagination, useRowKey, useRowSelection, useTable } from '@/hooks/table'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import { onMounted, nextTick } from 'vue'
|
||||
import { nextTick, onMounted } from 'vue'
|
||||
import { tableColumns } from './config'
|
||||
import { deleteUserFile, getUserFile, postUserFile } from '@/api/system/file_data'
|
||||
import { useProject } from '@/store/modules/get-project'
|
||||
import { minioURL } from '@/api/axios.config'
|
||||
|
||||
const pagination = usePagination(doRefresh)
|
||||
const { onSelectionChange } = useRowSelection()
|
||||
const table = useTable()
|
||||
const rowKey = useRowKey('id')
|
||||
const projectInfo: any = useProject()
|
||||
|
||||
function doRefresh() {
|
||||
getUserFile()
|
||||
@@ -67,25 +70,6 @@
|
||||
.catch(console.log)
|
||||
}
|
||||
|
||||
function downFile() {
|
||||
let aLink = document.createElement('a')
|
||||
aLink.href = '文件地址'
|
||||
}
|
||||
|
||||
// function onDownload(record: any) {
|
||||
// get({
|
||||
// url: userFilesDownload,
|
||||
// data: () => {
|
||||
// return {
|
||||
// project_id: record.project_id,
|
||||
// file_name: record.file_name,
|
||||
// }
|
||||
// },
|
||||
// })
|
||||
// .then((res) => {})
|
||||
// .catch(console.log)
|
||||
// }
|
||||
|
||||
function onDelete(record: any) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
@@ -141,6 +125,7 @@
|
||||
document.body.removeChild(aLink)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(async () => {
|
||||
doRefresh()
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
<p>元素下标:{{ item.sub ? item.sub : '-' }}</p>
|
||||
<div v-if="item.status === 0">
|
||||
<a-image
|
||||
:src="minioURL + '/failed_screenshot/' + item.picture_path"
|
||||
:src="minioURL + '/mango-file/failed_screenshot/' + item.picture_path"
|
||||
title="失败截图"
|
||||
width="260"
|
||||
style="margin-right: 67px; vertical-align: top"
|
||||
|
||||
@@ -272,7 +272,7 @@
|
||||
<p>元素下标:{{ item.sub ? item.sub : '-' }}</p>
|
||||
<div v-if="item.status === 0">
|
||||
<a-image
|
||||
:src="minioURL + '/failed_screenshot/' + item.picture_path"
|
||||
:src="minioURL + '/mango-file/failed_screenshot/' + item.picture_path"
|
||||
title="失败截图"
|
||||
width="260"
|
||||
style="margin-right: 67px; vertical-align: top"
|
||||
|
||||
@@ -72,9 +72,9 @@
|
||||
</template>
|
||||
|
||||
<template v-else-if="item.dataIndex === 'type'" #cell="{ record }">
|
||||
<a-tag :color="enumStore.colors[record.type]" size="small">{{
|
||||
enumStore.element_ope[record.type].title
|
||||
}}</a-tag>
|
||||
<a-tag :color="enumStore.colors[record.type]" size="small"
|
||||
>{{ enumStore.element_ope[record.type].title }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="item.dataIndex === 'actions'" #cell="{ record }">
|
||||
<a-button type="text" size="mini" @click="onTest(record)">调试</a-button>
|
||||
@@ -92,7 +92,7 @@
|
||||
<a-card title="最近一次步骤执行过程" style="overflow: hidden" :bordered="false">
|
||||
<a-collapse
|
||||
:default-active-key="data.eleResultKey"
|
||||
v-for="item of pageData.record.result_data?.element_result_list"
|
||||
v-for="item of data.result_data?.element_result_list"
|
||||
:bordered="false"
|
||||
:key="item.id"
|
||||
destroy-on-hide
|
||||
@@ -128,7 +128,7 @@
|
||||
<p>元素下标:{{ item.sub ? item.sub : '-' }}</p>
|
||||
<div v-if="item.status === 0">
|
||||
<a-image
|
||||
:src="minioURL + '/failed_screenshot/' + item.picture_path"
|
||||
:src="minioURL + '/mango-file/failed_screenshot/' + item.picture_path"
|
||||
title="失败截图"
|
||||
width="260"
|
||||
style="margin-right: 67px; vertical-align: top"
|
||||
@@ -285,12 +285,12 @@
|
||||
getUiPageStepsDetailed,
|
||||
getUiPageStepsDetailedAss,
|
||||
getUiPageStepsDetailedOpe,
|
||||
getUiPageStepsDetailedTest,
|
||||
postUiPageStepsDetailed,
|
||||
putUiPagePutStepSort,
|
||||
putUiPageStepsDetailed,
|
||||
getUiPageStepsDetailedTest,
|
||||
} from '@/api/uitest/page-steps-detailed'
|
||||
import { getUiStepsTest } from '@/api/uitest/page-steps'
|
||||
import { getUiSteps, getUiStepsTest } from '@/api/uitest/page-steps'
|
||||
import { getUiUiElementName } from '@/api/uitest/element'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { useEnum } from '@/store/modules/get-enum'
|
||||
@@ -314,6 +314,7 @@
|
||||
uiPageName: [],
|
||||
type: 0,
|
||||
plainOptions: [],
|
||||
result_data: {},
|
||||
})
|
||||
const visible1 = ref(false)
|
||||
|
||||
@@ -323,6 +324,7 @@
|
||||
border: 'none',
|
||||
overflow: 'hidden',
|
||||
})
|
||||
|
||||
function changeStatus(event: number) {
|
||||
data.type = event
|
||||
for (let i = formItems.length - 1; i >= 0; i--) {
|
||||
@@ -410,7 +412,7 @@
|
||||
deleteUiPageStepsDetailed(record.id, record.page_step.id)
|
||||
.then((res) => {
|
||||
Message.success(res.msg)
|
||||
getUiRunSort()
|
||||
doRefresh()
|
||||
})
|
||||
.catch(console.log)
|
||||
},
|
||||
@@ -474,7 +476,7 @@
|
||||
postUiPageStepsDetailed(value, route.query.id)
|
||||
.then((res) => {
|
||||
Message.success(res.msg)
|
||||
getUiRunSort()
|
||||
doRefresh()
|
||||
})
|
||||
.catch(console.log)
|
||||
} else {
|
||||
@@ -482,7 +484,7 @@
|
||||
putUiPageStepsDetailed(value, route.query.id)
|
||||
.then((res) => {
|
||||
Message.success(res.msg)
|
||||
getUiRunSort()
|
||||
doRefresh()
|
||||
})
|
||||
.catch(console.log)
|
||||
}
|
||||
@@ -493,7 +495,7 @@
|
||||
window.history.back()
|
||||
}
|
||||
|
||||
function getUiRunSort() {
|
||||
function doRefresh() {
|
||||
getUiPageStepsDetailed(route.query.id)
|
||||
.then((res) => {
|
||||
data.dataList = res.data
|
||||
@@ -637,13 +639,20 @@
|
||||
.catch(console.log)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(async () => {
|
||||
await getUiRunSortAss()
|
||||
await getUiRunSortOpe()
|
||||
await getEleName()
|
||||
getUiRunSort()
|
||||
})
|
||||
function doRefreshSteps(pageStepsId: any) {
|
||||
getUiSteps({ id: pageStepsId })
|
||||
.then((res) => {
|
||||
data.result_data = res.data[0].result_data
|
||||
})
|
||||
.catch(console.log)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
doRefresh()
|
||||
doRefreshSteps(pageData.record.id)
|
||||
getUiRunSortAss()
|
||||
getUiRunSortOpe()
|
||||
getEleName()
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FormItem } from '@/types/components'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
export const columns = reactive([
|
||||
export const columns: any = reactive([
|
||||
{
|
||||
title: '元素名称',
|
||||
dataIndex: 'name',
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
<a-card title="页面元素详情" :bordered="false">
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-upload @before-upload="beforeUpload" :show-file-list="false" />
|
||||
<a-button type="primary" size="small" @click="onDownload">下载模版</a-button>
|
||||
<a-upload
|
||||
type="primary"
|
||||
size="small"
|
||||
@before-upload="beforeUpload"
|
||||
:show-file-list="false"
|
||||
/>
|
||||
<a-button type="primary" size="small" @click="doAppend">增加</a-button>
|
||||
<a-button status="danger" size="small" @click="doResetSearch">返回</a-button>
|
||||
</a-space>
|
||||
@@ -182,8 +186,7 @@
|
||||
import { assForm, eleForm } from '@/views/uitest/page/elements/config'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { useEnum } from '@/store/modules/get-enum'
|
||||
import { postUserFile } from '@/api/system/file_data'
|
||||
import { minioURL } from '@/api/axios.config'
|
||||
import { baseURL } from '@/api/axios.config'
|
||||
const userStore = useUserStore()
|
||||
const enumStore = useEnum()
|
||||
|
||||
@@ -500,7 +503,7 @@
|
||||
}
|
||||
function onDownload() {
|
||||
const file_name = '元素批量上传模版.xlsx'
|
||||
const file_path = `${minioURL}/download?file_name=${file_name}`
|
||||
const file_path = `${baseURL}/download?file_name=${encodeURIComponent(file_name)}`
|
||||
let aLink = document.createElement('a')
|
||||
aLink.href = file_path
|
||||
aLink.download = file_name
|
||||
|
||||
Reference in New Issue
Block a user