Merge remote-tracking branch 'origin/master' into master

This commit is contained in:
Captain.B 2020-12-17 18:02:16 +08:00
commit fef2bc7e8a
79 changed files with 2935 additions and 966 deletions

View File

@ -1,153 +1,297 @@
package io.metersphere.api.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.*;
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
import io.metersphere.api.service.APITestService;
import io.metersphere.base.domain.ApiTest;
import io.metersphere.base.domain.Schedule;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao;
import io.metersphere.service.CheckOwnerService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import static io.metersphere.commons.utils.JsonPathUtils.getListJson;
@RestController
@RequestMapping(value = "/api")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
public class APITestController {
@Resource
private APITestService apiTestService;
@Resource
private CheckOwnerService checkownerService;
@GetMapping("recent/{count}")
public List<APITestResult> recentTest(@PathVariable int count) {
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
QueryAPITestRequest request = new QueryAPITestRequest();
request.setWorkspaceId(currentWorkspaceId);
request.setUserId(SessionUtils.getUserId());
PageHelper.startPage(1, count, true);
return apiTestService.recentTest(request);
}
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<APITestResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryAPITestRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
request.setProjectId(SessionUtils.getCurrentProjectId());
return PageUtils.setPageInfo(page, apiTestService.list(request));
}
@PostMapping("/list/ids")
public List<ApiTest> listByIds(@RequestBody QueryAPITestRequest request) {
return apiTestService.listByIds(request);
}
@GetMapping("/list/{projectId}")
public List<ApiTest> list(@PathVariable String projectId) {
checkownerService.checkProjectOwner(projectId);
return apiTestService.getApiTestByProjectId(projectId);
}
@PostMapping(value = "/schedule/update")
public void updateSchedule(@RequestBody Schedule request) {
apiTestService.updateSchedule(request);
}
@PostMapping(value = "/schedule/create")
public void createSchedule(@RequestBody Schedule request) {
apiTestService.createSchedule(request);
}
@PostMapping(value = "/create", consumes = {"multipart/form-data"})
public void create(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
apiTestService.create(request, file, bodyFiles);
}
@PostMapping(value = "/create/merge", consumes = {"multipart/form-data"})
public void mergeCreate(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "selectIds") List<String> selectIds) {
apiTestService.mergeCreate(request, file, selectIds);
}
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
checkownerService.checkApiTestOwner(request.getId());
apiTestService.update(request, file, bodyFiles);
}
@PostMapping(value = "/copy")
public void copy(@RequestBody SaveAPITestRequest request) {
apiTestService.copy(request);
}
@GetMapping("/get/{testId}")
public APITestResult get(@PathVariable String testId) {
checkownerService.checkApiTestOwner(testId);
return apiTestService.get(testId);
}
@PostMapping("/delete")
public void delete(@RequestBody DeleteAPITestRequest request) {
checkownerService.checkApiTestOwner(request.getId());
apiTestService.delete(request);
}
@PostMapping(value = "/run")
public String run(@RequestBody SaveAPITestRequest request) {
return apiTestService.run(request);
}
@PostMapping(value = "/run/debug", consumes = {"multipart/form-data"})
public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
return apiTestService.runDebug(request, file, bodyFiles);
}
@PostMapping(value = "/checkName")
public void checkName(@RequestBody SaveAPITestRequest request) {
apiTestService.checkName(request);
}
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public ApiTest testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
return apiTestService.apiTestImport(file, request);
}
@PostMapping("/dubbo/providers")
public List<DubboProvider> getProviders(@RequestBody RegistryCenter registry) {
return apiTestService.getProviders(registry);
}
@PostMapping("/list/schedule/{goPage}/{pageSize}")
public List<ScheduleDao> listSchedule(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryScheduleRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return apiTestService.listSchedule(request);
}
@PostMapping("/list/schedule")
public List<ScheduleDao> listSchedule(@RequestBody QueryScheduleRequest request) {
return apiTestService.listSchedule(request);
}
@PostMapping("/getJsonPaths")
public List<HashMap> getJsonPaths(@RequestBody QueryJsonPathRequest request) {
return getListJson(request.getJsonPath());
}
}
package io.metersphere.api.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.*;
import io.metersphere.api.dto.dataCount.ApiDataCountResult;
import io.metersphere.api.dto.dataCount.ExecutedCaseInfoResult;
import io.metersphere.api.dto.dataCount.request.ScheduleInfoRequest;
import io.metersphere.api.dto.dataCount.response.ApiDataCountDTO;
import io.metersphere.api.dto.dataCount.response.ExecutedCaseInfoDTO;
import io.metersphere.api.dto.dataCount.response.TaskInfoResult;
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
import io.metersphere.api.service.*;
import io.metersphere.base.domain.ApiTest;
import io.metersphere.base.domain.Schedule;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao;
import io.metersphere.service.CheckOwnerService;
import io.metersphere.service.ScheduleService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import static io.metersphere.commons.utils.JsonPathUtils.getListJson;
@RestController
@RequestMapping(value = "/api")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
public class APITestController {
@Resource
private APITestService apiTestService;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private CheckOwnerService checkownerService;
@Resource
private ApiTestCaseService apiTestCaseService;
@Resource
private ApiDefinitionExecResultService apiDefinitionExecResultService;
@Resource
private ApiAutomationService apiAutomationService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@Resource
private ScheduleService scheduleService;
@Resource
private APIReportService apiReportService;
@GetMapping("recent/{count}")
public List<APITestResult> recentTest(@PathVariable int count) {
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
QueryAPITestRequest request = new QueryAPITestRequest();
request.setWorkspaceId(currentWorkspaceId);
request.setUserId(SessionUtils.getUserId());
PageHelper.startPage(1, count, true);
return apiTestService.recentTest(request);
}
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<APITestResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryAPITestRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
request.setProjectId(SessionUtils.getCurrentProjectId());
return PageUtils.setPageInfo(page, apiTestService.list(request));
}
@PostMapping("/list/ids")
public List<ApiTest> listByIds(@RequestBody QueryAPITestRequest request) {
return apiTestService.listByIds(request);
}
@GetMapping("/list/{projectId}")
public List<ApiTest> list(@PathVariable String projectId) {
checkownerService.checkProjectOwner(projectId);
return apiTestService.getApiTestByProjectId(projectId);
}
@PostMapping(value = "/schedule/update")
public void updateSchedule(@RequestBody Schedule request) {
apiTestService.updateSchedule(request);
}
@PostMapping(value = "/schedule/create")
public void createSchedule(@RequestBody Schedule request) {
apiTestService.createSchedule(request);
}
@PostMapping(value = "/create", consumes = {"multipart/form-data"})
public void create(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
apiTestService.create(request, file, bodyFiles);
}
@PostMapping(value = "/create/merge", consumes = {"multipart/form-data"})
public void mergeCreate(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "selectIds") List<String> selectIds) {
apiTestService.mergeCreate(request, file, selectIds);
}
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
checkownerService.checkApiTestOwner(request.getId());
apiTestService.update(request, file, bodyFiles);
}
@PostMapping(value = "/copy")
public void copy(@RequestBody SaveAPITestRequest request) {
apiTestService.copy(request);
}
@GetMapping("/get/{testId}")
public APITestResult get(@PathVariable String testId) {
checkownerService.checkApiTestOwner(testId);
return apiTestService.get(testId);
}
@PostMapping("/delete")
public void delete(@RequestBody DeleteAPITestRequest request) {
checkownerService.checkApiTestOwner(request.getId());
apiTestService.delete(request);
}
@PostMapping(value = "/run")
public String run(@RequestBody SaveAPITestRequest request) {
return apiTestService.run(request);
}
@PostMapping(value = "/run/debug", consumes = {"multipart/form-data"})
public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
return apiTestService.runDebug(request, file, bodyFiles);
}
@PostMapping(value = "/checkName")
public void checkName(@RequestBody SaveAPITestRequest request) {
apiTestService.checkName(request);
}
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public ApiTest testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
return apiTestService.apiTestImport(file, request);
}
@PostMapping("/dubbo/providers")
public List<DubboProvider> getProviders(@RequestBody RegistryCenter registry) {
return apiTestService.getProviders(registry);
}
@PostMapping("/list/schedule/{goPage}/{pageSize}")
public List<ScheduleDao> listSchedule(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryScheduleRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return apiTestService.listSchedule(request);
}
@PostMapping("/list/schedule")
public List<ScheduleDao> listSchedule(@RequestBody QueryScheduleRequest request) {
return apiTestService.listSchedule(request);
}
@PostMapping("/getJsonPaths")
public List<HashMap> getJsonPaths(@RequestBody QueryJsonPathRequest request) {
return getListJson(request.getJsonPath());
}
@GetMapping("/apiCount/{projectId}")
public ApiDataCountDTO apiCount(@PathVariable String projectId) {
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
List<ApiDataCountResult> countResultList = apiDefinitionService.countProtocolByProjectID(projectId);
apiCountResult.countByApiDefinitionCountResult(countResultList);
long dateCountByCreateInThisWeek = apiDefinitionService.countByProjectIDAndCreateInThisWeek(projectId);
apiCountResult.setThisWeekAddedCount(dateCountByCreateInThisWeek);
return apiCountResult;
}
@GetMapping("/testCaseInfoCount/{projectId}")
public ApiDataCountDTO testCaseInfoCount(@PathVariable String projectId) {
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
List<ApiDataCountResult> countResultList = apiTestCaseService.countProtocolByProjectID(projectId);
apiCountResult.countByApiDefinitionCountResult(countResultList);
long dateCountByCreateInThisWeek = apiTestCaseService.countByProjectIDAndCreateInThisWeek(projectId);
apiCountResult.setThisWeekAddedCount(dateCountByCreateInThisWeek);
long executedInThisWeekCountNumber = apiDefinitionExecResultService.countByTestCaseIDInProjectAndExecutedInThisWeek(projectId);
apiCountResult.setThisWeekExecutedCount(executedInThisWeekCountNumber);
long executedCountNumber = apiDefinitionExecResultService.countByTestCaseIDInProject(projectId);
apiCountResult.setExecutedCount(executedCountNumber);
return apiCountResult;
}
@GetMapping("/testSceneInfoCount/{projectId}")
public ApiDataCountDTO testSceneInfoCount(@PathVariable String projectId) {
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
long scenarioCountNumber = apiAutomationService.countScenarioByProjectID(projectId);
apiCountResult.setAllApiDataCountNumber(scenarioCountNumber);
/**
* 本周新增通过测试场景的createTime
* 本周执行: 查询本周生成的测试报告
* 历史总执行查询所有的测试报告
* */
long dateCountByCreateInThisWeek = apiAutomationService.countScenarioByProjectIDAndCreatInThisWeek(projectId);
apiCountResult.setThisWeekAddedCount(dateCountByCreateInThisWeek);
long executedInThisWeekCountNumber = apiScenarioReportService.countByProjectIDAndCreateInThisWeek(projectId);
apiCountResult.setThisWeekExecutedCount(executedInThisWeekCountNumber);
long executedCountNumber = apiScenarioReportService.countByProjectID(projectId);
apiCountResult.setExecutedCount(executedCountNumber);
return apiCountResult;
}
@GetMapping("/scheduleTaskInfoCount/{workSpaceID}")
public ApiDataCountDTO scheduleTaskInfoCount(@PathVariable String workSpaceID) {
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
long allTaskCount = scheduleService.countTaskByWorkspaceIdAndGroup(workSpaceID,ScheduleGroup.API_TEST.name());
apiCountResult.setAllApiDataCountNumber(allTaskCount);
long taskCountInThisWeek = scheduleService.countTaskByWorkspaceIdAndGroupInThisWeek(workSpaceID,ScheduleGroup.API_TEST.name());
apiCountResult.setThisWeekAddedCount(taskCountInThisWeek);
long executedInThisWeekCountNumber = apiReportService.countByWorkspaceIdAndGroupAndCreateInThisWeek(workSpaceID,ScheduleGroup.API_TEST.name());
apiCountResult.setThisWeekExecutedCount(executedInThisWeekCountNumber);
long executedCountNumber = apiReportService.countByWorkspaceIdAndGroup(workSpaceID,ScheduleGroup.API_TEST.name());
apiCountResult.setExecutedCount(executedCountNumber);
return apiCountResult;
}
@GetMapping("/faliureCaseAboutTestPlan/{projectId}/{limitNumber}")
public List<ExecutedCaseInfoDTO> faliureCaseAboutTestPlan(@PathVariable String projectId, @PathVariable int limitNumber) {
List<ExecutedCaseInfoResult> selectDataList = apiDefinitionExecResultService.findFaliureCaseInfoByProjectIDAndLimitNumberInSevenDays(projectId,limitNumber);
List<ExecutedCaseInfoDTO> returnList = new ArrayList<>(limitNumber);
for(int dataIndex = 0;dataIndex < limitNumber;dataIndex ++){
ExecutedCaseInfoDTO dataDTO = new ExecutedCaseInfoDTO();
dataDTO.setSortIndex(dataIndex+1);
if(dataIndex<selectDataList.size()){
ExecutedCaseInfoResult selectData = selectDataList.get(dataIndex);
dataDTO.setCaseName(selectData.getCaseName());
dataDTO.setTestPlan(selectData.getTestPlan());
dataDTO.setFailureTimes(selectData.getFailureTimes());
}else {
dataDTO.setCaseName("");
dataDTO.setTestPlan("");
}
returnList.add(dataDTO);
}
return returnList;
}
@GetMapping("/runningTask/{workspaceID}")
public List<TaskInfoResult> runningTask(@PathVariable String workspaceID) {
List<TaskInfoResult> resultList = scheduleService.findRunningTaskInfoByWorkspaceID(workspaceID);
for (TaskInfoResult taskInfo :
resultList) {
Date nextExecutionTime = CronUtils.getNextTriggerTime(taskInfo.getRule());
if(nextExecutionTime!=null){
taskInfo.setNextExecutionTime(nextExecutionTime.getTime());
}
}
return resultList;
}
@PostMapping(value = "/schedule/updateEnableByPrimyKey")
public void updateScheduleEnableByPrimyKey(@RequestBody ScheduleInfoRequest request) {
Schedule schedule = scheduleService.getSchedule(request.getTaskID());
schedule.setEnable(request.isEnable());
apiTestService.updateSchedule(schedule);
}
}

View File

@ -2,9 +2,8 @@ package io.metersphere.api.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.ApiTestCaseResult;
import io.metersphere.api.dto.definition.SaveApiTestCaseRequest;
import io.metersphere.api.dto.ApiCaseBatchRequest;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.ApiTestCase;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
@ -35,7 +34,7 @@ public class ApiTestCaseController {
}
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<ApiTestCase>> listSimple(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiTestCaseRequest request) {
public Pager<List<ApiTestCaseDTO>> listSimple(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiTestCaseRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
return PageUtils.setPageInfo(page, apiTestCaseService.listSimple(request));
@ -56,9 +55,24 @@ public class ApiTestCaseController {
apiTestCaseService.delete(id);
}
@PostMapping("/removeToGc")
public void removeToGc(@RequestBody List<String> ids) {
apiTestCaseService.removeToGc(ids);
}
@GetMapping("/get/{id}")
public ApiTestCaseWithBLOBs get(@PathVariable String id) {
return apiTestCaseService.get(id);
}
}
@PostMapping("/batch/edit")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void editApiBath(@RequestBody ApiCaseBatchRequest request) {
apiTestCaseService.editApiBath(request);
}
@PostMapping("/deleteBatch")
public void deleteBatch(@RequestBody List<String> ids) {
apiTestCaseService.deleteBatch(ids);
}
}

View File

@ -0,0 +1,16 @@
package io.metersphere.api.dto;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.controller.request.OrderRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ApiCaseBatchRequest extends ApiTestCaseWithBLOBs {
private List<String> ids;
private List<OrderRequest> orders;
private String projectId;
}

View File

@ -0,0 +1,16 @@
package io.metersphere.api.dto.dataCount;
import lombok.Getter;
import lombok.Setter;
/**
* API数据统计查询结果类
*/
@Getter
@Setter
public class ApiDataCountResult {
//分组统计字段
private String groupField;
//数据统计
private long countNumber;
}

View File

@ -0,0 +1,18 @@
package io.metersphere.api.dto.dataCount;
import lombok.Getter;
import lombok.Setter;
/**
* 已执行的案例
*/
@Getter
@Setter
public class ExecutedCaseInfoResult {
//案例名称
private String caseName;
//所属测试计划
private String testPlan;
//失败次数
private Long failureTimes;
}

View File

@ -0,0 +1,16 @@
package io.metersphere.api.dto.dataCount.request;
import lombok.Getter;
import lombok.Setter;
/**
* @author song.tianyang
* @Date 2020/12/17 5:04 下午
* @Description
*/
@Getter
@Setter
public class ScheduleInfoRequest {
private String taskID;
private boolean enable;
}

View File

@ -0,0 +1,65 @@
package io.metersphere.api.dto.dataCount.response;
import io.metersphere.api.dto.dataCount.ApiDataCountResult;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.base.domain.ApiDefinition;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Locale;
/**
* 接口数据统计返回
*/
@Getter
@Setter
public class ApiDataCountDTO {
//接口统计
private long allApiDataCountNumber = 0;
//http接口统计
private long httpApiDataCountNumber = 0;
//rpc接口统计
private long rpcApiDataCountNumber = 0;
//tcp接口统计
private long tcpApiDataCountNumber = 0;
//sql接口统计
private long sqlApiDataCountNumber = 0;
//本周新增数量
private long thisWeekAddedCount = 0;
//本周执行数量
private long thisWeekExecutedCount = 0;
//历史总执行数量
private long executedCount = 0;
public ApiDataCountDTO(){}
/**
* 通过ApiDefinitionCountResult统计查询结果进行数据合计
* @param countResultList
*/
public void countByApiDefinitionCountResult(List<ApiDataCountResult> countResultList){
for (ApiDataCountResult countResult :
countResultList) {
switch (countResult.getGroupField().toUpperCase()){
case RequestType.DUBBO:
this.rpcApiDataCountNumber += countResult.getCountNumber();
break;
case RequestType.HTTP:
this.httpApiDataCountNumber += countResult.getCountNumber();
break;
case RequestType.SQL:
this.sqlApiDataCountNumber += countResult.getCountNumber();
break;
case RequestType.TCP:
this.tcpApiDataCountNumber += countResult.getCountNumber();
break;
default:
break;
}
allApiDataCountNumber += countResult.getCountNumber();
}
}
}

View File

@ -0,0 +1,20 @@
package io.metersphere.api.dto.dataCount.response;
import lombok.Getter;
import lombok.Setter;
/**
* 已执行的案例
*/
@Getter
@Setter
public class ExecutedCaseInfoDTO {
//排名
private int sortIndex;
//案例名称
private String caseName;
//所属测试计划
private String testPlan;
//失败次数
private Long failureTimes;
}

View File

@ -0,0 +1,30 @@
package io.metersphere.api.dto.dataCount.response;
import lombok.Getter;
import lombok.Setter;
import org.python.antlr.ast.Str;
/**
* 任务信息 返回DTO
*/
@Getter
@Setter
public class TaskInfoResult {
//序号
private int index;
//任务ID
private String taskID;
//场景名称
private String scenario;
//规则
private String rule;
//任务状态
private boolean taskStatus;
//下次执行时间
private Long nextExecutionTime;
//创建人
private String creator;
//更新时间
private Long updateTime;
}

View File

@ -0,0 +1,15 @@
package io.metersphere.api.dto.definition;
import io.metersphere.base.domain.ApiTestCase;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ApiTestCaseDTO extends ApiTestCase {
private String moduleId;
private String path;
private String protocol;
private String updateUser;
private String createUser;
}

View File

@ -5,11 +5,11 @@ import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class ApiTestCaseRequest {
private String id;
private String projectId;
private String priority;
@ -17,6 +17,8 @@ public class ApiTestCaseRequest {
private String environmentId;
private String workspaceId;
private String apiDefinitionId;
private String status;
private List<String> moduleIds;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
}

View File

@ -15,6 +15,7 @@ import io.metersphere.base.mapper.ext.ExtApiTestReportMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
@ -25,14 +26,12 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.*;
@Service
@Transactional(rollbackFor = Exception.class)
@ -131,7 +130,7 @@ public class APIReportService {
String startTime = jsonRequestResults.getJSONObject(j).getString("startTime");
String name = jsonRequestResults.getJSONObject(j).getString("name");
String url = jsonRequestResults.getJSONObject(j).getString("url");
if (StringUtils.isBlank(url)){
if (StringUtils.isBlank(url)) {
//如果非http请求不入库
continue;
}
@ -139,7 +138,7 @@ public class APIReportService {
apiDataView.setId(UUID.randomUUID().toString());
apiDataView.setReportId(reportId);
apiDataView.setApiName(name);
apiDataView.setUrl(StringUtils.substringBefore(url,"?"));
apiDataView.setUrl(StringUtils.substringBefore(url, "?"));
apiDataView.setResponseTime(responseTime);
apiDataView.setStartTime(sdf.format(new Date(Long.parseLong(startTime))));
apiDataView.setResponseCode(responseCode);
@ -149,8 +148,9 @@ public class APIReportService {
} catch (Exception e) {
LogUtil.error(e);
}
apiDataViewMapper.insertListApiData(listApiDataView);
if (listApiDataView.size() > 0) {
apiDataViewMapper.insertListApiData(listApiDataView);
}
}
public String create(ApiTest test, String triggerMode) {
@ -210,4 +210,21 @@ public class APIReportService {
apiTestReportExample.createCriteria().andIdIn(reportRequest.getIds());
apiTestReportMapper.deleteByExample(apiTestReportExample);
}
public long countByWorkspaceIdAndGroupAndCreateInThisWeek(String workspaceID, String group) {
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date firstTime = startAndEndDateInWeek.get("firstTime");
Date lastTime = startAndEndDateInWeek.get("lastTime");
if(firstTime==null || lastTime == null){
return 0;
}else {
return apiTestReportMapper.countByProjectIDAndCreateInThisWeek(workspaceID,group,firstTime.getTime(),lastTime.getTime());
}
}
public long countByWorkspaceIdAndGroup(String workspaceID, String group) {
return apiTestReportMapper.countByWorkspaceIdAndGroup(workspaceID,group);
}
}

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.client.utils.StringUtils;
import io.github.ningyu.jmeter.plugin.dubbo.sample.ProviderService;
import io.metersphere.api.dto.*;
import io.metersphere.api.dto.dataCount.ApiDataCountResult;
import io.metersphere.api.dto.parse.ApiImport;
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
import io.metersphere.api.jmeter.JMeterService;

View File

@ -22,6 +22,7 @@ import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator;
@ -355,4 +356,20 @@ public class ApiAutomationService {
return "success";
}
public long countScenarioByProjectID(String projectId) {
return apiScenarioMapper.countByProjectID(projectId);
}
public long countScenarioByProjectIDAndCreatInThisWeek(String projectId) {
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date firstTime = startAndEndDateInWeek.get("firstTime");
Date lastTime = startAndEndDateInWeek.get("lastTime");
if(firstTime==null || lastTime == null){
return 0;
}else {
return apiScenarioMapper.countByProjectIDAndCreatInThisWeek(projectId,firstTime.getTime(),lastTime.getTime());
}
}
}

View File

@ -1,25 +1,32 @@
package io.metersphere.api.service;
import com.alibaba.fastjson.JSON;
import io.metersphere.api.dto.dataCount.ExecutedCaseInfoResult;
import io.metersphere.api.jmeter.TestResult;
import io.metersphere.base.domain.ApiDefinitionExecResult;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.SessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiDefinitionExecResultService {
@Resource
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
@Resource
private ExtApiDefinitionExecResultMapper extApiDefinitionExecResultMapper;
public void saveApiResult(TestResult result) {
result.getScenarios().get(0).getRequestResults().forEach(item -> {
// 清理原始资源每个执行 保留一条结果
extApiDefinitionExecResultMapper.deleteByResourceId(item.getName());
ApiDefinitionExecResult saveResult = new ApiDefinitionExecResult();
saveResult.setId(UUID.randomUUID().toString());
saveResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
@ -32,4 +39,40 @@ public class ApiDefinitionExecResultService {
apiDefinitionExecResultMapper.insert(saveResult);
});
}
public long countByTestCaseIDInProjectAndExecutedInThisWeek(String projectId) {
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date firstTime = startAndEndDateInWeek.get("firstTime");
Date lastTime = startAndEndDateInWeek.get("lastTime");
if(firstTime==null || lastTime == null){
return 0;
}else {
return apiDefinitionExecResultMapper.countByProjectIDAndCreateInThisWeek(projectId,firstTime.getTime(),lastTime.getTime());
}
}
public long countByTestCaseIDInProject(String projectId) {
return apiDefinitionExecResultMapper.countByTestCaseIDInProject(projectId);
}
public List<ExecutedCaseInfoResult> findFaliureCaseInfoByProjectIDAndLimitNumberInSevenDays(String projectId, int limitNumber) {
//获取7天之前的日期
Date startDay = DateUtils.dateSum(new Date(),-6);
//将日期转化为 00:00:00 的时间戳
Date startTime = null;
try{
startTime = DateUtils.getDayStartTime(startDay);
}catch (Exception e){
}
if(startTime==null){
return new ArrayList<>(0);
}else {
return apiDefinitionExecResultMapper.findFaliureCaseInfoByProjectIDAndExecuteTimeAndLimitNumber(projectId,startTime.getTime(),limitNumber);
}
}
}

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.ReferenceDTO;
import io.metersphere.api.dto.dataCount.ApiDataCountResult;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.scenario.request.RequestType;
@ -23,10 +24,7 @@ import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.i18n.Translator;
import io.metersphere.service.FileService;
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
@ -43,10 +41,7 @@ import sun.security.util.Cache;
import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -388,4 +383,30 @@ public class ApiDefinitionService {
apiDefinitionMapper.updateByExampleSelective(definitionWithBLOBs, definitionExample);
}
/**
* 数据统计-接口类型
* @param projectId 项目ID
* @return
*/
public List<ApiDataCountResult> countProtocolByProjectID(String projectId) {
return apiDefinitionMapper.countProtocolByProjectID(projectId);
}
/**
* 统计本周创建的数据总量
* @param projectId
* @return
*/
public long countByProjectIDAndCreateInThisWeek(String projectId) {
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date firstTime = startAndEndDateInWeek.get("firstTime");
Date lastTime = startAndEndDateInWeek.get("lastTime");
if(firstTime==null || lastTime == null){
return 0;
}else {
return apiDefinitionMapper.countByProjectIDAndCreateInThisWeek(projectId,firstTime.getTime(),lastTime.getTime());
}
}
}

View File

@ -166,7 +166,9 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
ApiModuleDTO nodeTree = request.getNodeTree();
List<ApiModule> updateNodes = new ArrayList<>();
if (nodeTree == null) {
return;
}
buildUpdateDefinition(nodeTree, apiModule, updateNodes, "/", "0", nodeTree.getLevel());
updateNodes = updateNodes.stream()
@ -179,8 +181,7 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
}
private void buildUpdateDefinition(ApiModuleDTO rootNode, List<ApiDefinitionResult> apiDefinitions,
List<ApiModule> updateNodes, String rootPath, String pId, int level) {
List<ApiModule> updateNodes, String rootPath, String pId, int level) {
rootPath = rootPath + rootNode.getName();
if (level > 8) {

View File

@ -13,7 +13,6 @@ import io.metersphere.base.mapper.ApiScenarioModuleMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioModuleMapper;
import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.i18n.Translator;
import io.metersphere.service.NodeTreeService;
import org.apache.commons.lang3.StringUtils;
@ -24,7 +23,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@ -140,7 +142,9 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
ApiScenarioModuleDTO nodeTree = request.getNodeTree();
List<ApiScenarioModule> updateNodes = new ArrayList<>();
if (nodeTree == null) {
return;
}
buildUpdateDefinition(nodeTree, apiScenarios, updateNodes, "/", "0", nodeTree.getLevel());
updateNodes = updateNodes.stream()

View File

@ -14,6 +14,7 @@ import io.metersphere.base.mapper.ApiScenarioReportMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.i18n.Translator;
import org.apache.commons.lang3.StringUtils;
@ -24,7 +25,9 @@ import sun.security.util.Cache;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Service
@ -205,4 +208,20 @@ public class ApiScenarioReportService {
apiScenarioReportMapper.deleteByExample(apiTestReportExample);
}
public long countByProjectID(String projectId) {
return apiScenarioReportMapper.countByProjectID(projectId);
}
public long countByProjectIDAndCreateInThisWeek(String projectId) {
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date firstTime = startAndEndDateInWeek.get("firstTime");
Date lastTime = startAndEndDateInWeek.get("lastTime");
if(firstTime==null || lastTime == null){
return 0;
}else {
return apiScenarioReportMapper.countByProjectIDAndCreateInThisWeek(projectId,firstTime.getTime(),lastTime.getTime());
}
}
}

View File

@ -1,23 +1,24 @@
package io.metersphere.api.service;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.definition.ApiDefinitionRequest;
import io.metersphere.api.dto.dataCount.ApiDataCountResult;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.ApiTestCaseResult;
import io.metersphere.api.dto.definition.SaveApiTestCaseRequest;
import io.metersphere.api.dto.ApiCaseBatchRequest;
import io.metersphere.api.dto.definition.*;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiTestCaseMapper;
import io.metersphere.base.mapper.ApiTestFileMapper;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ext.ExtApiTestCaseMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.i18n.Translator;
import io.metersphere.notice.domain.UserDetail;
import io.metersphere.service.FileService;
import io.metersphere.service.QuotaService;
import io.metersphere.service.UserService;
import org.aspectj.util.FileUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -26,10 +27,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
@Service
@ -38,6 +36,8 @@ public class ApiTestCaseService {
@Resource
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
private UserService userService;
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ApiTestFileMapper apiTestFileMapper;
@ -53,9 +53,22 @@ public class ApiTestCaseService {
return extApiTestCaseMapper.list(request);
}
public List<ApiTestCase> listSimple(ApiTestCaseRequest request) {
public List<ApiTestCaseDTO> listSimple(ApiTestCaseRequest request) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
List<ApiTestCase> apiTestCases = extApiTestCaseMapper.listSimple(request);
List<ApiTestCaseDTO> apiTestCases = extApiTestCaseMapper.listSimple(request);
if (CollectionUtils.isEmpty(apiTestCases)) {
return apiTestCases;
}
List<String> userIds = new ArrayList();
userIds.addAll(apiTestCases.stream().map(ApiTestCaseDTO::getCreateUserId).collect(Collectors.toList()));
userIds.addAll(apiTestCases.stream().map(ApiTestCaseDTO::getUpdateUserId).collect(Collectors.toList()));
if (!CollectionUtils.isEmpty(userIds)) {
Map<String, User> userMap = userService.queryNameByIds(userIds);
apiTestCases.forEach(caseResult -> {
caseResult.setCreateUser(userMap.get(caseResult.getCreateUserId()).getName());
caseResult.setUpdateUser(userMap.get(caseResult.getUpdateUserId()).getName());
});
}
return apiTestCases;
}
@ -215,4 +228,41 @@ public class ApiTestCaseService {
fileService.deleteFileByIds(fileIds);
}
}
public void removeToGc(List<String> ids) {
// todo
}
public void editApiBath(ApiCaseBatchRequest request) {
ApiTestCaseExample apiDefinitionExample = new ApiTestCaseExample();
apiDefinitionExample.createCriteria().andIdIn(request.getIds());
ApiTestCaseWithBLOBs apiDefinitionWithBLOBs = new ApiTestCaseWithBLOBs();
BeanUtils.copyBean(apiDefinitionWithBLOBs, request);
apiDefinitionWithBLOBs.setUpdateTime(System.currentTimeMillis());
apiTestCaseMapper.updateByExampleSelective(apiDefinitionWithBLOBs, apiDefinitionExample);
}
public void deleteBatch(List<String> ids) {
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andIdIn(ids);
apiTestCaseMapper.deleteByExample(example);
}
public List<ApiDataCountResult> countProtocolByProjectID(String projectId) {
return apiTestCaseMapper.countProtocolByProjectID(projectId);
}
public long countByProjectIDAndCreateInThisWeek(String projectId) {
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date firstTime = startAndEndDateInWeek.get("firstTime");
Date lastTime = startAndEndDateInWeek.get("lastTime");
if(firstTime==null || lastTime == null){
return 0;
}else {
return apiTestCaseMapper.countByProjectIDAndCreateInThisWeek(projectId,firstTime.getTime(),lastTime.getTime());
}
}
}

View File

@ -1,8 +1,10 @@
package io.metersphere.base.mapper;
import io.metersphere.api.dto.dataCount.ExecutedCaseInfoResult;
import io.metersphere.base.domain.ApiDefinitionExecResult;
import io.metersphere.base.domain.ApiDefinitionExecResultExample;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@ -34,4 +36,42 @@ public interface ApiDefinitionExecResultMapper {
int updateByPrimaryKeyWithBLOBs(ApiDefinitionExecResult record);
int updateByPrimaryKey(ApiDefinitionExecResult record);
@Select({
"SELECT count(id) AS countNumber FROM api_definition_exec_result ",
"WHERE resource_id IN ( ",
"SELECT testCase.id FROM api_test_case testCase ",
"WHERE testCase.project_id = #{projectId}) ",
"and start_time BETWEEN #{firstDayTimestamp} AND #{lastDayTimestamp} "
})
long countByProjectIDAndCreateInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
@Select({
"SELECT count(id) AS countNumber FROM api_definition_exec_result ",
"WHERE resource_id IN ( ",
"SELECT testCase.id FROM api_test_case testCase ",
"WHERE testCase.project_id = #{projectId}) ",
})
long countByTestCaseIDInProject(String projectId);
// AS testPlan FROM ( SELECT apiCase.id AS testCaseID,apiCase.`name` AS testCaseNa' at line 1
@Select({
"SELECT testCase.testCaseName AS caseName,testCase.testPlanName AS testPlan ,caseErrorCountData.dataCountNumber AS failureTimes FROM ( ",
"SELECT apiCase.id AS testCaseID,apiCase.`name` AS testCaseName,group_concat(testPlan.`name`) AS testPlanName FROM api_test_case apiCase ",
"LEFT JOIN test_plan testPlan ON testPlan.api_ids like concat('%\"',apiCase.id,'\"%') ",
"GROUP BY apiCase.id ",
"ORDER BY apiCase.create_time DESC ",
")testCase ",
"INNER JOIN ( ",
"SELECT resource_id AS testCaseID,COUNT(id) AS dataCountNumber,start_time AS executeTime FROM api_definition_exec_result ",
"WHERE resource_id IN ( ",
"SELECT id FROM api_test_case WHERE project_id = #{projectId} ",
") and `status` = 'error' GROUP BY resource_id ",
") caseErrorCountData ON caseErrorCountData.testCaseID =testCase.testCaseID ",
"WHERE caseErrorCountData.executeTime >= #{startTimestamp} ",
"ORDER BY caseErrorCountData.dataCountNumber DESC ",
"limit #{limitNumber} "
})
List<ExecutedCaseInfoResult> findFaliureCaseInfoByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber);
}

View File

@ -1,9 +1,12 @@
package io.metersphere.base.mapper;
import io.metersphere.api.dto.dataCount.ApiDataCountResult;
import io.metersphere.api.dto.dataCount.response.ApiDataCountDTO;
import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.base.domain.ApiDefinitionExample;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@ -35,4 +38,14 @@ public interface ApiDefinitionMapper {
int updateByPrimaryKeyWithBLOBs(ApiDefinitionWithBLOBs record);
int updateByPrimaryKey(ApiDefinition record);
@Select("SELECT protocol AS groupField,count(id) AS countNumber FROM api_definition WHERE project_id = #{0} GROUP BY protocol;")
List<ApiDataCountResult> countProtocolByProjectID(String projectId);
@Select({
"SELECT count(id) AS countNumber FROM api_definition ",
"WHERE project_id = #{projectId} ",
"AND create_time BETWEEN #{firstDayTimestamp} AND #{lastDayTimestamp} "
})
long countByProjectIDAndCreateInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
}

View File

@ -4,6 +4,7 @@ import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface ApiScenarioMapper {
long countByExample(ApiScenarioExample example);
@ -33,4 +34,14 @@ public interface ApiScenarioMapper {
int updateByPrimaryKeyWithBLOBs(ApiScenario record);
int updateByPrimaryKey(ApiScenario record);
@Select("SELECT COUNT(id) AS countNumber FROM api_scenario WHERE project_id = #{0} ")
long countByProjectID(String projectId);
@Select({
"SELECT count(id) AS countNumber FROM api_scenario ",
"WHERE project_id = #{projectId} ",
"AND create_time BETWEEN #{firstDayTimestamp} AND #{lastDayTimestamp} "
})
long countByProjectIDAndCreatInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
}

View File

@ -4,6 +4,7 @@ import io.metersphere.base.domain.ApiScenarioReport;
import io.metersphere.base.domain.ApiScenarioReportExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface ApiScenarioReportMapper {
long countByExample(ApiScenarioReportExample example);
@ -27,4 +28,14 @@ public interface ApiScenarioReportMapper {
int updateByPrimaryKeySelective(ApiScenarioReport record);
int updateByPrimaryKey(ApiScenarioReport record);
@Select("SELECT count(id) AS countNumber FROM api_scenario_report WHERE project_id = #{0} ")
long countByProjectID(String projectId);
@Select({
"SELECT count(id) AS countNumber FROM api_scenario_report ",
"WHERE project_id = #{projectId} ",
"AND create_time BETWEEN #{firstDayTimestamp} AND #{lastDayTimestamp} "
})
long countByProjectIDAndCreateInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
}

View File

@ -1,9 +1,11 @@
package io.metersphere.base.mapper;
import io.metersphere.api.dto.dataCount.ApiDataCountResult;
import io.metersphere.base.domain.ApiTestCase;
import io.metersphere.base.domain.ApiTestCaseExample;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@ -35,4 +37,20 @@ public interface ApiTestCaseMapper {
int updateByPrimaryKeyWithBLOBs(ApiTestCaseWithBLOBs record);
int updateByPrimaryKey(ApiTestCase record);
@Select({
"SELECT apiDef.protocol AS groupField,COUNT(testCase.id) AS countNumber FROM api_test_case testCase ",
"INNER JOIN api_definition apiDef ON testCase.api_definition_id = apiDef.id ",
"WHERE testCase.project_id = #{0} ",
"GROUP BY apiDef.protocol "
})
List<ApiDataCountResult> countProtocolByProjectID(String projectId);
@Select({
"SELECT count(testCase.id) AS countNumber FROM api_test_case testCase ",
"INNER JOIN api_definition apiDef ON testCase.api_definition_id = apiDef.id ",
"WHERE testCase.project_id = #{projectId} ",
"AND testCase.create_time BETWEEN #{firstDayTimestamp} AND #{lastDayTimestamp} "
})
long countByProjectIDAndCreateInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
}

View File

@ -4,6 +4,7 @@ import io.metersphere.base.domain.ApiTestReport;
import io.metersphere.base.domain.ApiTestReportExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface ApiTestReportMapper {
long countByExample(ApiTestReportExample example);
@ -27,4 +28,21 @@ public interface ApiTestReportMapper {
int updateByPrimaryKeySelective(ApiTestReport record);
int updateByPrimaryKey(ApiTestReport record);
@Select({
"SELECT COUNT(testReportDetail.report_id) AS countNumber FROM api_test_report_detail testReportDetail ",
"INNER JOIN `schedule` sch ON sch.resource_id = testReportDetail.test_id ",
"INNER JOIN api_test_report testReport ON testReportDetail.report_id = testReport.id ",
"WHERE workspace_id = #{workspaceID} AND `group` = #{group} ",
})
long countByWorkspaceIdAndGroup(@Param("workspaceID") String workspaceID, @Param("group")String group);
@Select({
"SELECT COUNT(testReportDetail.report_id) AS countNumber FROM api_test_report_detail testReportDetail ",
"INNER JOIN `schedule` sch ON sch.resource_id = testReportDetail.test_id ",
"INNER JOIN api_test_report testReport ON testReportDetail.report_id = testReport.id ",
"WHERE workspace_id = #{workspaceID} AND `group` = #{group} ",
"AND testReport.create_time BETWEEN #{startTime} and #{endTime} ",
})
long countByProjectIDAndCreateInThisWeek(@Param("workspaceID") String workspaceID, @Param("group")String group, @Param("startTime") long startTime, @Param("endTime")long endTime);
}

View File

@ -1,9 +1,13 @@
package io.metersphere.base.mapper;
import io.metersphere.api.dto.dataCount.response.TaskInfoResult;
import io.metersphere.base.domain.Schedule;
import io.metersphere.base.domain.ScheduleExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.python.antlr.ast.Str;
public interface ScheduleMapper {
long countByExample(ScheduleExample example);
@ -33,4 +37,25 @@ public interface ScheduleMapper {
int updateByPrimaryKeyWithBLOBs(Schedule record);
int updateByPrimaryKey(Schedule record);
@Select("SELECT COUNT(id) AS countNumber FROM `schedule` WHERE `workspace_id` = #{workspaceId} AND `group` = #{group} ")
long countTaskByWorkspaceIdAndGroup(@Param("workspaceId") String workspaceId,@Param("group") String group);
@Select({
"SELECT COUNT(id) AS countNumber FROM `schedule` ",
"WHERE workspace_id = #{workspaceId} ",
"AND `group` = #{group} ",
"AND create_time BETWEEN #{startTime} and #{endTime}; "
})
long countTaskByWorkspaceIdAndGroupAndCreateTimeRange(@Param("workspaceId")String workspaceId,@Param("group") String group, @Param("startTime") long startTime, @Param("endTime") long endTime);
@Select({
"SELECT apiTest.`name` AS scenario,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime ",
"FROM api_test apiTest ",
"INNER JOIN `schedule` sch ON apiTest.id = sch.resource_id ",
"INNER JOIN `user` u ON u.id = sch.user_id ",
"WHERE sch.`enable` = true AND sch.workspace_id = #{0,jdbcType=VARCHAR}"
})
List<TaskInfoResult> findRunningTaskInfoByWorkspaceID(String workspaceID);
}

View File

@ -1,5 +1,6 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.ApiTestCaseResult;
import io.metersphere.base.domain.ApiTestCase;
@ -10,5 +11,5 @@ import java.util.List;
public interface ExtApiTestCaseMapper {
List<ApiTestCaseResult> list(@Param("request") ApiTestCaseRequest request);
List<ApiTestCase> listSimple(@Param("request") ApiTestCaseRequest request);
List<ApiTestCaseDTO> listSimple(@Param("request") ApiTestCaseRequest request);
}

View File

@ -208,19 +208,54 @@
</if>
</select>
<select id="listSimple" resultType="io.metersphere.base.domain.ApiTestCase">
<select id="listSimple" resultType="io.metersphere.api.dto.definition.ApiTestCaseDTO">
select
c.id, c.project_id, c.name, c.api_definition_id, c.priority, c.description, c.create_user_id, c.update_user_id, c.create_time, c.update_time,
a.moudule_id as moudule_id,
from api_test_case c
left join api_definition a
on c.api_definition_id = a.id
where c.project_id = #{request.projectId}
c.id, c.project_id, c.name, c.api_definition_id, c.priority, c.description, c.create_user_id, c.update_user_id, c.create_time, c.update_time,
a.module_id, a.path, a.protocol
from
api_test_case c
inner join
api_definition a
on
c.api_definition_id = a.id
<choose>
<when test="request.status == 'Trash'">
and a.status = 'Trash'
</when>
<otherwise>
and a.status != 'Trash'
</otherwise>
</choose>
where
c.project_id = #{request.projectId}
<if test="request.name != null and request.name!=''">
and c.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
AND a.module_id in
and a.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'priority'">
and c.priority in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
<if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">
${order.name} ${order.type}
</foreach>
</if>
</select>
</mapper>

View File

@ -3,9 +3,11 @@ package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.User;
import io.metersphere.controller.request.UserRequest;
import io.metersphere.notice.domain.UserDetail;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface ExtUserMapper {
@ -19,4 +21,6 @@ public interface ExtUserMapper {
List<UserDetail> queryTypeByIds(List<String> userIds);
@MapKey("id")
Map<String, User> queryNameByIds(List<String> userIds);
}

View File

@ -62,4 +62,14 @@
select id, name, email, last_organization_id, last_workspace_id from `user` where id like CONCAT('%', #{condition},'%') or email like CONCAT('%', #{condition},'%') limit 100;
</select>
<select id="queryNameByIds" resultType="io.metersphere.base.domain.User">
select id, name
from `user`
WHERE id IN
<foreach collection="list" item="id" index="index"
open="(" close=")" separator=",">
#{id}
</foreach>
</select>
</mapper>

View File

@ -0,0 +1,62 @@
package io.metersphere.commons.utils;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.TriggerBuilder;
import java.util.Date;
/**
* @author song.tianyang
* @Date 2020/12/17 4:06 下午
* @Description CRON解析类
*/
public class CronUtils {
/**
* 解析表达式获取CronTrigger
* @param cron
* @return
*/
public static CronTrigger getCronTrigger(String cron) {
if (!CronExpression.isValidExpression(cron)) {
throw new RuntimeException("cron :" + cron + "表达式解析错误");
}
return TriggerBuilder.newTrigger().withIdentity("Caclulate Date").withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
}
/**
* 获取以指定时间为开始时间的下一次执行时间
* @param cron
* @param start
* @return
*/
public static Date getNextTriggerTime(String cron, Date start) {
if (start == null) {
return getNextTriggerTime(cron);
}else{
CronTrigger trigger = getCronTrigger(cron);
return trigger.getFireTimeAfter(start);
}
}
/**
* 获取以当前日期为准的下一次执行时间
* @param cron
* @return
*/
public static Date getNextTriggerTime(String cron) {
Date date = null;
try{
CronTrigger trigger = getCronTrigger(cron);
Date startDate = trigger.getStartTime();
date = trigger.getFireTimeAfter(startDate);
}catch (Exception e){
}
return date;
}
}

View File

@ -0,0 +1,121 @@
package io.metersphere.commons.utils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class DateUtils {
public static final String DATE_PATTERM = "yyyy-MM-dd";
public static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static Date getDate(String dateString) throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERM);
return dateFormat.parse(dateString);
}
public static Date getTime(String timeString) throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat(TIME_PATTERN);
return dateFormat.parse(timeString);
}
public static String getDateString(Date date) throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERM);
return dateFormat.format(date);
}
public static String getDateString(long timeStamp) throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERM);
return dateFormat.format(timeStamp);
}
public static String getTimeString(Date date) throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat(TIME_PATTERN);
return dateFormat.format(date);
}
public static String getTimeString(long timeStamp) throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat(TIME_PATTERN);
return dateFormat.format(timeStamp);
}
public static Date dateSum (Date date,int countDays){
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH,countDays);
return calendar.getTime();
}
/**
* 获取入参日期所在周的周一周末日期 日期对应的时间为当日的零点
*
* @return Map<String, String>(2); key取值范围firstTime/lastTime
*/
public static Map<String, Date> getWeedFirstTimeAndLastTime(Date date) {
Map<String, Date> returnMap = new HashMap<>();
Calendar calendar = Calendar.getInstance();
//Calendar默认一周的开始是周日业务需求从周一开始算所以要"+1"
int weekDayAdd = 1;
try {
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_WEEK, calendar.getActualMinimum(Calendar.DAY_OF_WEEK));
calendar.add(Calendar.DAY_OF_MONTH,weekDayAdd);
//第一天的时分秒是 00:00:00 这里直接取日期默认就是零点零分
Date thisWeekFirstTime = getDate(getDateString(calendar.getTime()));
calendar.clear();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_WEEK, calendar.getActualMaximum(Calendar.DAY_OF_WEEK));
calendar.add(Calendar.DAY_OF_MONTH,weekDayAdd);
//最后一天的时分秒应当是23:59:59 处理方式是增加一天计算日期再-1
calendar.add(Calendar.DAY_OF_MONTH,1);
Date nextWeekFirstDay = getDate(getDateString(calendar.getTime()));
Date thisWeekLastTime = getTime(getTimeString(nextWeekFirstDay.getTime()-1));
returnMap.put("firstTime", thisWeekFirstTime);
returnMap.put("lastTime", thisWeekLastTime);
} catch (Exception e) {
e.printStackTrace();
}
return returnMap;
}
public static void main(String[] args) throws Exception {
System.out.println("start:");
Date paramTime = getTime(getTimeString(new Long("1607672440731")));
Map<String, Date> weekDate = getWeedFirstTimeAndLastTime(paramTime);
for (Map.Entry<String, Date> entry :
weekDate.entrySet()) {
System.out.println(entry.getKey() + ":" + getTimeString(entry.getValue())+":"+entry.getValue().getTime());
}
long countTimeLong = new Long("1607672440731");
System.out.println(getTimeString(--countTimeLong));
}
/**
* 获取当天的起始时间Date
* @param time 指定日期 2020-12-13 06:12:42
* @return 当天起始时间 2020-12-13 00:00:00
* @throws Exception
*/
public static Date getDayStartTime(Date time) throws Exception {
return getDate(getDateString(time));
}
}

View File

@ -53,6 +53,7 @@ public class PerformanceTestController {
public Pager<List<LoadTestDTO>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestPlanRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
request.setProjectId(SessionUtils.getCurrentProjectId());
return PageUtils.setPageInfo(page, performanceTestService.list(request));
}

View File

@ -79,7 +79,6 @@ public class PerformanceTestService {
public List<LoadTestDTO> list(QueryTestPlanRequest request) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
request.setProjectId(SessionUtils.getCurrentProjectId());
return extLoadTestMapper.list(request);
}

View File

@ -1,6 +1,7 @@
package io.metersphere.service;
import com.alibaba.fastjson.JSON;
import io.metersphere.api.dto.dataCount.response.TaskInfoResult;
import io.metersphere.base.domain.Schedule;
import io.metersphere.base.domain.ScheduleExample;
import io.metersphere.base.domain.User;
@ -9,6 +10,7 @@ import io.metersphere.base.mapper.ScheduleMapper;
import io.metersphere.base.mapper.UserMapper;
import io.metersphere.base.mapper.ext.ExtScheduleMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
@ -25,6 +27,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@ -162,4 +165,26 @@ public class ScheduleService {
schedule.setUserName(userMap.get(schedule.getUserId()));
});
}
public long countTaskByWorkspaceIdAndGroup(String workspaceId,String group) {
return scheduleMapper.countTaskByWorkspaceIdAndGroup(workspaceId,group);
}
public long countTaskByWorkspaceIdAndGroupInThisWeek(String workspaceID, String group) {
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date firstTime = startAndEndDateInWeek.get("firstTime");
Date lastTime = startAndEndDateInWeek.get("lastTime");
if(firstTime==null || lastTime == null){
return 0;
}else {
return scheduleMapper.countTaskByWorkspaceIdAndGroupAndCreateTimeRange(workspaceID,group,firstTime.getTime(),lastTime.getTime());
}
}
public List<TaskInfoResult> findRunningTaskInfoByWorkspaceID(String workspaceID) {
List<TaskInfoResult> runningTaskInfoList = scheduleMapper.findRunningTaskInfoByWorkspaceID(workspaceID);
return runningTaskInfoList;
}
}

View File

@ -66,6 +66,10 @@ public class UserService {
return extUserMapper.queryTypeByIds(userIds);
}
public Map<String, User> queryNameByIds(List<String> userIds) {
return extUserMapper.queryNameByIds(userIds);
}
/* public List<String> queryEmailByIds(List<String> userIds) {
return extUserMapper.queryTypeByIds(userIds);
}*/

View File

@ -5,7 +5,7 @@
<span v-if="!debug"><el-input size="mini" style="width: 200px" v-model="report.name"/> </span>
<span class="time"> {{ report.createTime | timestampFormatDate }}</span>
<el-button :disabled="isReadOnly" class="export-button" plain type="primary" size="mini" @click="handleExport(report.name)" style="margin-right: 10px">
<el-button v-if="!debug" :disabled="isReadOnly" class="export-button" plain type="primary" size="mini" @click="handleExport(report.name)" style="margin-right: 10px">
{{$t('test_track.plan_view.export_report')}}
</el-button>

View File

@ -27,7 +27,9 @@
<ms-dropdown v-if="isSqlType" :commands="sqlModes" :default-command="mode" @command="sqlModeChange"/>
</template>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.console')" name="console" class="pane">
<pre>{{response.console}}</pre>
</el-tab-pane>
</el-tabs>
</el-collapse-transition>
</div>

View File

@ -35,26 +35,31 @@
:name="item.name">
<!-- 列表集合 -->
<ms-api-list
v-if="item.type === 'list'"
v-if="item.type === 'list' && isApiListEnable"
:current-protocol="currentProtocol"
:visible="visible"
:currentRow="currentRow"
:select-node-ids="selectNodeIds"
:trash-enable="trashEnable"
:is-api-list-enable="isApiListEnable"
@editApi="editApi"
@handleCase="handleCase"
@showExecResult="showExecResult"
@isApiListEnableChange="isApiListEnableChange"
ref="apiList"/>
<!--测试用例列表-->
<api-case-simple-list
v-if="item.type === 'list' && !isApiListEnable"
:current-protocol="currentProtocol"
:visible="visible"
:currentRow="currentRow"
:select-node-ids="selectNodeIds"
:trash-enable="trashEnable"
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange"
@handleCase="handleCase"
@showExecResult="showExecResult"
ref="apiList"/>
<!--<api-case-simple-list-->
<!--v-if="item.type === 'list'"-->
<!--:current-protocol="currentProtocol"-->
<!--:visible="visible"-->
<!--:currentRow="currentRow"-->
<!--:select-node-ids="selectNodeIds"-->
<!--:trash-enable="trashEnable"-->
<!--@handleCase="handleCase"-->
<!--@showExecResult="showExecResult"-->
<!--ref="apiList"/>-->
<!-- 添加/编辑测试窗口-->
<div v-else-if="item.type=== 'ADD'" class="ms-api-div">
@ -147,6 +152,7 @@
type: "list",
closable: false
}],
isApiListEnable: true
}
},
watch: {
@ -155,6 +161,9 @@
}
},
methods: {
isApiListEnableChange(data) {
this.isApiListEnable = data;
},
handleCommand(e) {
switch (e) {
case "ADD":
@ -241,7 +250,7 @@
downloadFile("导出API.json", JSON.stringify(obj));
},
refresh(data) {
this.$refs.apiList[0].initApiTable(data);
this.$refs.apiList[0].initTable(data);
},
setTabTitle(data) {
for (let index in this.apiTabs) {

View File

@ -80,6 +80,18 @@
if (!this.request.hashTree) {
this.request.hashTree = [];
}
//
if (this.response.body) {
let body = new Body();
Object.assign(body, this.response.body);
if (!body.binary) {
body.binary = [];
}
if (!body.kvs) {
body.kvs = [];
}
this.response.body = body;
}
},
methods: {
runTest(data) {

View File

@ -64,9 +64,9 @@
</el-col>
<!--<el-col v-if="type === 'body'" class="kv-select">-->
<!--<el-input :disabled="isReadOnly" v-model="item.contentType" size="small"-->
<!--@change="change" :placeholder="$t('api_test.request.content_type')" show-word-limit>-->
<!--</el-input>-->
<!--<el-input :disabled="isReadOnly" v-model="item.contentType" size="small"-->
<!--@change="change" :placeholder="$t('api_test.request.content_type')" show-word-limit>-->
<!--</el-input>-->
<!--</el-col>-->
<el-col class="kv-delete">

View File

@ -94,7 +94,7 @@
saveApi(saveAs) {
this.$refs['httpForm'].validate((valid) => {
if (valid) {
if(this.httpForm.path.match(/\s/)!=null){
if (this.httpForm.path && this.httpForm.path.match(/\s/) != null) {
this.$error(this.$t("api_test.definition.request.path_valid_info"));
return false;
}

View File

@ -182,11 +182,13 @@
if (!this.body.type) {
this.body.type = BODY_TYPE.FORM_DATA;
}
this.body.kvs.forEach(param => {
if (!param.type) {
param.type = 'text';
}
});
if (this.body.kvs) {
this.body.kvs.forEach(param => {
if (!param.type) {
param.type = 'text';
}
});
}
}
}
</script>

View File

@ -13,12 +13,13 @@
<el-col :span="api.protocol==='HTTP'? 4:0">
<div class="variable-combine" style="margin-left: 10px">{{api.path ===null ? " " : api.path}}</div>
</el-col>
<el-col :span="2">
<el-col :span="2" v-if="!isCaseEdit">
<div>{{$t('test_track.plan_view.case_count')}}{{apiCaseList.length}}</div>
</el-col>
<el-col :span="3">
<div>
<el-select size="small" :placeholder="$t('api_test.definition.request.grade_info')" v-model="condition.priority"
:disabled="isCaseEdit"
class="ms-api-header-select" @change="getApiTest">
<el-option v-for="grd in priorities" :key="grd.id" :label="grd.name" :value="grd.id"/>
</el-select>
@ -48,25 +49,19 @@
<el-col :span="3">
<div class="ms-api-header-select">
<el-input size="small" :placeholder="$t('api_test.definition.request.select_case')"
:disabled="isCaseEdit"
v-model="condition.name" @blur="getApiTest" @keyup.enter.native="getApiTest"/>
</div>
</el-col>
<el-col :span="2">
<el-dropdown size="small" split-button type="primary" class="ms-api-header-select" @click="addCase"
<el-dropdown size="small" split-button type="primary" class="ms-api-header-select" @click="addCase" :disabled="isReadOnly || isCaseEdit"
@command="handleCommand">
+{{$t('api_test.definition.request.case')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="run">{{$t('commons.test')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
<el-col :span="2">
<button type="button" aria-label="Close" class="el-card-btn" @click="close()"><i
class="el-dialog__close el-icon el-icon-close"></i></button>
</el-col>
</el-row>
</el-card>
@ -95,6 +90,7 @@
priorities: Array,
apiCaseList: Array,
isReadOnly: Boolean,
isCaseEdit: Boolean,
condition: {
type: Object,
default() {
@ -145,9 +141,6 @@
getApiTest() {
this.$emit('getApiTest');
},
close() {
this.$emit('close');
},
addCase() {
this.$emit('addCase');
},

View File

@ -37,10 +37,10 @@
<ms-tip-button @click="singleRun(apiCase)" :tip="$t('api_test.run')" icon="el-icon-video-play"
style="background-color: #409EFF;color: white" size="mini" :disabled="!apiCase.id" circle/>
<ms-tip-button @click="copyCase(apiCase)" :tip="$t('commons.copy')" icon="el-icon-document-copy"
size="mini" :disabled="!apiCase.id" circle/>
size="mini" :disabled="!apiCase.id || isCaseEdit" circle/>
<ms-tip-button @click="deleteCase(index,apiCase)" :tip="$t('commons.delete')" icon="el-icon-delete"
size="mini" :disabled="!apiCase.id" circle/>
<ms-api-extend-btns :row="apiCase"/>
size="mini" :disabled="!apiCase.id || isCaseEdit" circle/>
<ms-api-extend-btns :is-case-edit="isCaseEdit" :row="apiCase"/>
</el-col>
<el-col :span="3">
@ -85,7 +85,6 @@
import MsDubboBasisParameters from "../request/dubbo/BasisParameters";
import MsApiExtendBtns from "../reference/ApiExtendBtns";
export default {
name: "ApiCaseItem",
components: {
@ -132,7 +131,8 @@
default() {
return {}
}
}
},
isCaseEdit: Boolean,
},
watch: {
},
@ -149,7 +149,7 @@
},
copyCase(data) {
let obj = {name: "copy_" + data.name, priority: data.priority, active: false, request: data.request};
this.apiCaseList.unshift(obj);
this.$emit('copyCase', obj);
},
selectTestCase(item, $event) {
@ -211,6 +211,9 @@
return true;
}
},
showExecResult(data) {
this.$emit('showExecResult', data);
},
getBodyUploadFiles(row) {
let bodyUploadFiles = [];
row.bodyUploadIds = [];

View File

@ -1,12 +1,11 @@
<template>
<div v-if="visible">
<ms-drawer :size="40" direction="bottom">
<ms-drawer :size="40" @close="apiCaseClose" direction="bottom">
<template v-slot:header>
<api-case-header
:api="api"
@getApiTest="getApiTest"
@setEnvironment="setEnvironment"
@close="apiCaseClose"
@addCase="addCase"
@batchRun="batchRun"
:condition="condition"
@ -14,6 +13,7 @@
:apiCaseList="apiCaseList"
:is-read-only="isReadOnly"
:project-id="projectId"
:is-case-edit="isCaseEdit"
/>
</template>
@ -21,10 +21,13 @@
<el-main v-loading="batchLoading" style="overflow: auto">
<div v-for="(item,index) in apiCaseList" :key="index">
<api-case-item v-loading="singleLoading && singleRunId === item.id"
@refresh="getApiTest"
@singleRun="singleRun"
:api="api"
:api-case="item" :index="index"/>
@refresh="refresh"
@singleRun="singleRun"
@copyCase="copyCase"
@showExecResult="showExecResult"
:is-case-edit="isCaseEdit"
:api="api"
:api-case="item" :index="index"/>
</div>
</el-main>
</el-container>
@ -78,6 +81,7 @@
runData: [],
reportId: "",
projectId: "",
testCaseId: "",
checkedCases: new Set(),
visible: false,
condition: {},
@ -103,9 +107,16 @@
this.getApiTest();
}
},
computed: {
isCaseEdit() {
return this.testCaseId ? true : false;
}
},
methods: {
open(api) {
open(api, testCaseId) {
this.api = api;
// testCaseId
this.testCaseId = testCaseId;
this.getApiTest();
this.visible = true;
},
@ -139,10 +150,19 @@
this.$emit('refresh');
},
refresh(data) {
this.getApiTest();
this.$emit('refresh');
},
getApiTest() {
if (this.api) {
this.condition.projectId = this.projectId;
this.condition.apiDefinitionId = this.api.id;
if (this.isCaseEdit) {
this.condition.id = this.testCaseId;
} else {
this.condition.apiDefinitionId = this.api.id;
}
this.result = this.$post("/api/testcase/list", this.condition, response => {
for (let index in response.data) {
let test = response.data[index];
@ -152,9 +172,9 @@
}
}
this.apiCaseList = response.data;
if (this.apiCaseList.length == 0) {
this.addCase();
}
// if (this.apiCaseList.length == 0) {
// this.addCase();
// }
});
}
},
@ -173,6 +193,10 @@
}
},
copyCase(data) {
this.apiCaseList.unshift(data);
},
handleClose() {
this.visible = false;
},
@ -219,9 +243,6 @@
} else {
this.$warning("没有可执行的用例!");
}
},
refresh() {
this.getApiTest();
}
}
}

View File

@ -1,13 +1,16 @@
<template>
<div>
<api-list-container>
<api-list-container
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange">
<el-input placeholder="搜索" @blur="search" class="search-input" size="small" v-model="condition.name"/>
<el-table v-loading="result.loading"
border
:data="tableData" row-key="id" class="test-content adjust-table"
@select-all="handleSelectAll"
@select="handleSelect" :height="screenHeight">
border
:data="tableData" row-key="id" class="test-content adjust-table"
@select-all="handleSelectAll"
@filter-change="filter"
@sort-change="sort"
@select="handleSelect" :height="screenHeight">
<el-table-column type="selection"/>
<el-table-column width="40" :resizable="false" align="center">
<template v-slot:default="scope">
@ -34,11 +37,15 @@
show-overflow-tooltip/>
<el-table-column
prop="createUserId"
prop="createUser"
:label="'创建人'"
show-overflow-tooltip/>
<el-table-column width="160" :label="$t('api_test.definition.api_last_time')" prop="updateTime">
<el-table-column
sortable="custom"
width="160"
:label="$t('api_test.definition.api_last_time')"
prop="updateTime">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
@ -47,18 +54,18 @@
<el-table-column :label="$t('commons.operating')" min-width="130" align="center">
<template v-slot:default="scope">
<el-button type="text" @click="reductionApi(scope.row)" v-if="trashEnable">恢复</el-button>
<el-button type="text" @click="editCase(scope.row)" v-else>{{$t('commons.edit')}}</el-button>
<!--<el-button type="text" @click="reductionApi(scope.row)" v-if="trashEnable">恢复</el-button>-->
<el-button type="text" @click="handleTestCase(scope.row)" v-if="!trashEnable">{{$t('commons.edit')}}</el-button>
<el-button type="text" @click="handleDelete(scope.row)" style="color: #F56C6C">{{$t('commons.delete')}}</el-button>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="initApiTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</api-list-container>
<api-case-list @refresh="initApiTable" @showExecResult="showExecResult" :currentApi="selectApi" ref="caseList"/>
<api-case-list @showExecResult="showExecResult" @refresh="initTable" :currentApi="selectCase" ref="caseList"/>
<!--批量编辑-->
<ms-batch-edit ref="batchEdit" @batchEdit="batchEdit" :typeArr="typeArr" :value-arr="valueArr"/>
</div>
@ -79,11 +86,12 @@
import MsBottomContainer from "../BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../basis/BatchEdit";
import {API_METHOD_COLOUR, REQ_METHOD, API_STATUS} from "../../model/JsonData";
import {API_METHOD_COLOUR, CASE_PRIORITY} from "../../model/JsonData";
import {getCurrentProjectID} from "@/common/js/utils";
import ApiListContainer from "./ApiListContainer";
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
import ApiCaseList from "../case/ApiCaseList";
import {_filter, _sort} from "../../../../../../common/js/utils";
export default {
name: "ApiCaseSimpleList",
@ -106,7 +114,7 @@
data() {
return {
condition: {},
selectApi: {},
selectCase: {},
result: {},
moduleId: "",
deletePath: "/test/case/delete",
@ -116,14 +124,16 @@
{name: this.$t('api_test.definition.request.batch_edit'), handleClick: this.handleEditBatch}
],
typeArr: [
{id: 'status', name: this.$t('api_test.definition.api_case_status')},
{id: 'method', name: this.$t('api_test.definition.api_type')},
{id: 'userId', name: this.$t('api_test.definition.api_principal')},
{id: 'priority', name: this.$t('test_track.case.priority')},
],
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
valueArr: {
status: API_STATUS,
method: REQ_METHOD,
userId: [],
priority: CASE_PRIORITY,
},
methodColorMap: new Map(API_METHOD_COLOUR),
tableData: [],
@ -144,34 +154,37 @@
trashEnable: {
type: Boolean,
default: false,
}
},
isApiListEnable: Boolean
},
created: function () {
this.projectId = getCurrentProjectID();
this.initApiTable();
this.getMaintainerOptions();
this.initTable();
},
watch: {
selectNodeIds() {
this.initApiTable();
this.initTable();
},
currentProtocol() {
this.initApiTable();
this.initTable();
},
trashEnable() {
if (this.trashEnable) {
this.initApiTable();
this.initTable();
}
},
},
methods: {
initApiTable() {
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
},
initTable() {
this.selectRows = new Set();
this.condition.filters = ["Prepare", "Underway", "Completed"];
// this.condition.filters = ["Prepare", "Underway", "Completed"];
this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
this.condition.filters = ["Trash"];
this.condition.status = "Trash";
this.condition.moduleIds = [];
}
if (this.projectId != null) {
@ -210,6 +223,22 @@
})
}
},
showExecResult(row) {
this.visible = false;
this.$emit('showExecResult', row);
},
filter(filters) {
_filter(filters, this.condition);
this.initTable();
},
sort(column) {
//
if (this.condition.orders) {
this.condition.orders = [];
}
_sort(column, this.condition);
this.initTable();
},
handleSelectAll(selection) {
if (selection.length > 0) {
if (selection.length === 1) {
@ -230,81 +259,66 @@
}
},
search() {
this.initApiTable();
this.initTable();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
// handleTestCase(api) {
// this.selectApi = api;
// let request = {};
// if (Object.prototype.toString.call(api.request).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
// request = api.request;
// } else {
// request = JSON.parse(api.request);
// }
// if (!request.hashTree) {
// request.hashTree = [];
// }
// this.selectApi.url = request.path;
// this.$refs.caseList.open(this.selectApi);
// },
editCase(row) {
// this.$emit('editCase', row);
this.$get('/api/definition/' + row.api_definition_id, (response) => {
})
// this.selectApi = api;
// let request = {};
// if (Object.prototype.toString.call(api.request).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
// request = api.request;
// } else {
// request = JSON.parse(api.request);
// }
// if (!request.hashTree) {
// request.hashTree = [];
// }
// this.selectApi.url = request.path;
// this.$refs.caseList.open(this.selectApi);
handleTestCase(testCase) {
this.$get('/api/definition/get/' + testCase.apiDefinitionId, (response) => {
let api = response.data;
let selectApi = api;
let request = {};
if (Object.prototype.toString.call(api.request).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
request = api.request;
} else {
request = JSON.parse(api.request);
}
if (!request.hashTree) {
request.hashTree = [];
}
selectApi.url = request.path;
this.$refs.caseList.open(selectApi, testCase.id);
});
},
reductionApi(row) {
let ids = [row.id];
this.$post('/api/definition/reduction/', ids, () => {
this.$post('/api/testcase/reduction/', ids, () => {
this.$success(this.$t('commons.save_success'));
this.search();
});
},
handleDeleteBatch() {
if (this.trashEnable) {
// if (this.trashEnable) {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/api/definition/deleteBatch/', ids, () => {
this.$post('/api/testcase/deleteBatch/', ids, () => {
this.selectRows.clear();
this.initApiTable();
this.initTable();
this.$success(this.$t('commons.delete_success'));
});
}
}
});
} else {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/api/definition/removeToGc/', ids, () => {
this.selectRows.clear();
this.initApiTable();
this.$success(this.$t('commons.delete_success'));
});
}
}
});
}
// } else {
// this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
// confirmButtonText: this.$t('commons.confirm'),
// callback: (action) => {
// if (action === 'confirm') {
// let ids = Array.from(this.selectRows).map(row => row.id);
// this.$post('/api/testcase/removeToGc/', ids, () => {
// this.selectRows.clear();
// this.initTable();
// this.$success(this.$t('commons.delete_success'));
// });
// }
// }
// });
// }
},
handleEditBatch() {
this.$refs.batchEdit.open();
@ -315,39 +329,31 @@
let param = {};
param[form.type] = form.value;
param.ids = ids;
this.$post('/api/definition/batch/edit', param, () => {
this.$post('/api/testcase/batch/edit', param, () => {
this.$success(this.$t('commons.save_success'));
this.initApiTable();
this.initTable();
});
},
handleDelete(api) {
if (this.trashEnable) {
this.$get('/api/definition/delete/' + api.id, () => {
handleDelete(apiCase) {
// if (this.trashEnable) {
this.$get('/api/testcase/delete/' + apiCase.id, () => {
this.$success(this.$t('commons.delete_success'));
this.initApiTable();
this.initTable();
});
return;
}
this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + api.name + " ", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = [api.id];
this.$post('/api/definition/removeToGc/', ids, () => {
this.$success(this.$t('commons.delete_success'));
this.initApiTable();
});
}
}
});
},
getColor(enable, method) {
if (enable) {
return this.methodColorMap.get(method);
}
},
showExecResult(row) {
this.$emit('showExecResult', row);
// }
// this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + apiCase.name + " ", '', {
// confirmButtonText: this.$t('commons.confirm'),
// callback: (action) => {
// if (action === 'confirm') {
// let ids = [apiCase.id];
// this.$post('/api/testcase/removeToGc/', ids, () => {
// this.$success(this.$t('commons.delete_success'));
// this.initTable();
// });
// }
// }
// });
}
},
}

View File

@ -1,6 +1,8 @@
<template>
<div>
<api-list-container>
<api-list-container
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange">
<el-input placeholder="搜索" @blur="search" class="search-input" size="small" v-model="condition.name"/>
<el-table v-loading="result.loading"
@ -80,10 +82,10 @@
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="initApiTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</api-list-container>
<ms-api-case-list @refresh="initApiTable" @showExecResult="showExecResult" :currentApi="selectApi" ref="caseList"/>
<ms-api-case-list @refresh="initTable" @showExecResult="showExecResult" :currentApi="selectApi" ref="caseList"/>
<!--批量编辑-->
<ms-batch-edit ref="batchEdit" @batchEdit="batchEdit" :typeArr="typeArr" :value-arr="valueArr"/>
</div>
@ -166,28 +168,32 @@
trashEnable: {
type: Boolean,
default: false,
}
},
isApiListEnable: Boolean
},
created: function () {
this.projectId = getCurrentProjectID();
this.initApiTable();
this.initTable();
this.getMaintainerOptions();
},
watch: {
selectNodeIds() {
this.initApiTable();
this.initTable();
},
currentProtocol() {
this.initApiTable();
this.initTable();
},
trashEnable() {
if (this.trashEnable) {
this.initApiTable();
this.initTable();
}
},
},
methods: {
initApiTable() {
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
},
initTable() {
this.selectRows = new Set();
this.condition.filters = ["Prepare", "Underway", "Completed"];
@ -252,7 +258,7 @@
}
},
search() {
this.initApiTable();
this.initTable();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
@ -277,7 +283,7 @@
let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/api/definition/deleteBatch/', ids, () => {
this.selectRows.clear();
this.initApiTable();
this.initTable();
this.$success(this.$t('commons.delete_success'));
});
}
@ -291,7 +297,7 @@
let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/api/definition/removeToGc/', ids, () => {
this.selectRows.clear();
this.initApiTable();
this.initTable();
this.$success(this.$t('commons.delete_success'));
});
}
@ -310,7 +316,7 @@
param.ids = ids;
this.$post('/api/definition/batch/edit', param, () => {
this.$success(this.$t('commons.save_success'));
this.initApiTable();
this.initTable();
});
},
handleTestCase(api) {
@ -331,7 +337,7 @@
if (this.trashEnable) {
this.$get('/api/definition/delete/' + api.id, () => {
this.$success(this.$t('commons.delete_success'));
this.initApiTable();
this.initTable();
});
return;
}
@ -342,7 +348,7 @@
let ids = [api.id];
this.$post('/api/definition/removeToGc/', ids, () => {
this.$success(this.$t('commons.delete_success'));
this.initApiTable();
this.initTable();
});
}
}

View File

@ -1,8 +1,8 @@
<template>
<el-card class="card-content" v-if="isShow">
<el-button-group>
<el-button plain size="small" icon="el-icon-tickets" :class="{active: activeButton == 'api'}" @click="click('api')"></el-button>
<el-button plain size="small" icon="el-icon-paperclip" :class="{active: activeButton == 'case'}" @click="click('case')"></el-button>
<el-button plain size="small" icon="el-icon-tickets" :class="{active: isApiListEnable}" @click="apiChange('api')"></el-button>
<el-button plain class="case-button" size="small" icon="el-icon-paperclip" :class="{active: !isApiListEnable}" @click="caseChange('case')"></el-button>
</el-button-group>
<slot></slot>
</el-card>
@ -13,29 +13,32 @@
name: "ApiListContainer",
data() {
return {
activeButton: 'api',
isShow: true
}
},
props: {
isApiListEnable: Boolean
},
methods: {
click(type) {
this.activeButton = type;
// this.reload();
apiChange() {
this.$emit('isApiListEnableChange', true);
},
// reload() {
// this.isShow = false;
// this.$nextTick(() => {
// this.isShow = true;
// })
// }
caseChange() {
this.$emit('isApiListEnableChange', false);
}
}
}
</script>
<style scoped>
/*.active {*/
/*background-color: #409EFF;*/
/*}*/
.active {
border: solid 1px #6d317c;
}
.case-button {
border-left: solid 1px #6d317c;
}
</style>

View File

@ -4,7 +4,7 @@
<ms-node-tree
v-loading="result.loading"
:tree-nodes="data"
:type="'edit'"
:type="isReadOnly ? 'view' : 'edit'"
@add="add"
@edit="edit"
@drag="drag"
@ -16,6 +16,7 @@
<api-module-header
:condition="condition"
:current-module="currentModule"
:is-read-only="isReadOnly"
@exportAPI="exportAPI"
@saveAsEdit="saveAsEdit"
@refresh="refresh"
@ -60,6 +61,14 @@
currentModule: {},
}
},
props: {
isReadOnly: {
type: Boolean,
default() {
return false
}
}
},
mounted() {
this.projectId = getCurrentProjectID();
this.$emit('protocolChange', this.condition.protocol);

View File

@ -9,9 +9,9 @@
:disabled="item.disabled">
</el-option>
</el-select>
<el-input class="filter-input" :placeholder="$t('test_track.module.search')" v-model="condition.filterText" size="small">
<el-input class="filter-input" :class="{'read-only': isReadOnly}" :placeholder="$t('test_track.module.search')" v-model="condition.filterText" size="small">
<template v-slot:append>
<el-dropdown size="small" split-button type="primary" class="ms-api-button" @click="handleCommand('add-api')"
<el-dropdown v-if="!isReadOnly" size="small" split-button type="primary" class="ms-api-button" @click="handleCommand('add-api')"
@command="handleCommand">
<el-button icon="el-icon-folder-add" @click="addApi"></el-button>
<el-dropdown-menu slot="dropdown">
@ -61,6 +61,12 @@
default() {
return {}
}
},
isReadOnly: {
type: Boolean,
default() {
return false
}
}
},
methods: {
@ -105,8 +111,13 @@
height: 30px;
}
.read-only {
width: 150px !important;
}
.filter-input {
width: 175px;
padding-left: 3px;
}
</style>

View File

@ -5,7 +5,7 @@
</el-link>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="ref">{{ $t('api_test.automation.view_ref') }}</el-dropdown-item>
<el-dropdown-item command="add_plan">{{ $t('api_test.automation.batch_add_plan') }}</el-dropdown-item>
<el-dropdown-item :disabled="isCaseEdit" command="add_plan">{{ $t('api_test.automation.batch_add_plan') }}</el-dropdown-item>
</el-dropdown-menu>
<ms-reference-view ref="viewRef"/>
<!--测试计划-->
@ -23,7 +23,8 @@
name: "MsApiExtendBtns",
components: {MsReferenceView, MsTestPlanList},
props: {
row: Object
row: Object,
isCaseEdit: Boolean,
},
data() {
return {

View File

@ -1,100 +1,102 @@
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
<el-row>
<el-col :span="21">
<!-- HTTP 请求参数 -->
<div style="border:1px #DCDFE6 solid; height: 100%;border-radius: 4px ;width: 100%" v-loading="isReloadData">
<el-tabs v-model="activeName" class="request-tabs">
<!-- 请求头-->
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.request.headers')" placement="top-start" slot="label">
<div>
<el-row>
<el-col :span="21">
<!-- HTTP 请求参数 -->
<div style="border:1px #DCDFE6 solid; height: 100%;border-radius: 4px ;width: 100%" v-loading="isReloadData">
<el-tabs v-model="activeName" class="request-tabs">
<!-- 请求头-->
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.request.headers')" placement="top-start" slot="label">
<span>{{$t('api_test.request.headers')}}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="headers.length>1">
<div class="el-step__icon-inner">{{headers.length-1}}</div>
</div>
</span>
</el-tooltip>
<ms-api-key-value :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers"/>
</el-tab-pane>
</el-tooltip>
<ms-api-key-value :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers"/>
</el-tab-pane>
<!--query 参数-->
<el-tab-pane :label="$t('api_test.definition.request.query_param')" name="parameters">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.query_info')" placement="top-start" slot="label">
<!--query 参数-->
<el-tab-pane :label="$t('api_test.definition.request.query_param')" name="parameters">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.query_info')" placement="top-start" slot="label">
<span>{{$t('api_test.definition.request.query_param')}}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.arguments.length>1">
<div class="el-step__icon-inner">{{request.arguments.length-1}}</div>
</div></span>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd"> {{$t("commons.batch_add")}}</el-link>
</el-row>
<ms-api-variable :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.arguments"/>
</el-tab-pane>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd"> {{$t("commons.batch_add")}}</el-link>
</el-row>
<ms-api-variable :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.arguments"/>
</el-tab-pane>
<!--REST 参数-->
<el-tab-pane :label="$t('api_test.definition.request.rest_param')" name="rest">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.rest_info')" placement="top-start" slot="label">
<!--REST 参数-->
<el-tab-pane :label="$t('api_test.definition.request.rest_param')" name="rest">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.rest_info')" placement="top-start" slot="label">
<span>
{{$t('api_test.definition.request.rest_param')}}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.rest.length>1">
<div class="el-step__icon-inner">{{request.rest.length-1}}</div>
</div>
</span>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd"> {{$t("commons.batch_add")}}</el-link>
</el-row>
<ms-api-variable :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.rest"/>
</el-tab-pane>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd"> {{$t("commons.batch_add")}}</el-link>
</el-row>
<ms-api-variable :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.rest"/>
</el-tab-pane>
<!--请求体-->
<el-tab-pane v-if="isBodyShow" :label="$t('api_test.request.body')" name="body">
<ms-api-body @headersChange="reloadBody" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :headers="headers" :body="request.body"/>
</el-tab-pane>
<!--请求体-->
<el-tab-pane v-if="isBodyShow" :label="$t('api_test.request.body')" name="body">
<ms-api-body @headersChange="reloadBody" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :headers="headers" :body="request.body"/>
</el-tab-pane>
<!-- 认证配置 -->
<el-tab-pane :label="$t('api_test.definition.request.auth_config')" name="authConfig">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.auth_config_info')" placement="top-start" slot="label">
<span>{{$t('api_test.definition.request.auth_config')}}</span>
</el-tooltip>
<!-- 认证配置 -->
<el-tab-pane :label="$t('api_test.definition.request.auth_config')" name="authConfig">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.auth_config_info')" placement="top-start" slot="label">
<span>{{$t('api_test.definition.request.auth_config')}}</span>
</el-tooltip>
<ms-api-auth-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
<ms-api-auth-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
<el-tab-pane label="其他设置" name="advancedConfig">
<ms-api-advanced-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
<el-tab-pane label="其他设置" name="advancedConfig">
<ms-api-advanced-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
</el-tabs>
</div>
<div v-if="!referenced">
<div v-for="row in request.hashTree" :key="row.id">
<!-- 前置脚本 -->
<ms-jsr233-processor v-if="row.label ==='JSR223 PreProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.pre_script')" style-type="color: #B8741A;background-color: #F9F1EA"
:jsr223-processor="row"/>
<!--后置脚本-->
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/>
<!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
<!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
</el-tabs>
</div>
<div v-if="!referenced">
<div v-for="row in request.hashTree" :key="row.id">
<!-- 前置脚本 -->
<ms-jsr233-processor v-if="row.label ==='JSR223 PreProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.pre_script')" style-type="color: #B8741A;background-color: #F9F1EA"
:jsr223-processor="row"/>
<!--后置脚本-->
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/>
<!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
<!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
</div>
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
</div>
</el-col>
<!--操作按钮-->
<el-col :span="3" class="ms-left-cell" v-if="!referenced">
<el-button class="ms-left-buttion" size="small" @click="addPre">+{{$t('api_test.definition.request.pre_script')}}</el-button>
<br/>
<el-button class="ms-left-buttion" size="small" @click="addPost">+{{$t('api_test.definition.request.post_script')}}</el-button>
<br/>
<el-button class="ms-left-buttion" size="small" @click="addAssertions">+{{$t('api_test.definition.request.assertions_rule')}}</el-button>
<br/>
<el-button class="ms-left-buttion" size="small" @click="addExtract">+{{$t('api_test.definition.request.extract_param')}}</el-button>
</el-col>
</el-row>
</div>
</el-col>
<!--操作按钮-->
<el-col :span="3" class="ms-left-cell" v-if="!referenced">
<el-button class="ms-left-buttion" size="small" @click="addPre">+{{$t('api_test.definition.request.pre_script')}}</el-button>
<br/>
<el-button class="ms-left-buttion" size="small" @click="addPost">+{{$t('api_test.definition.request.post_script')}}</el-button>
<br/>
<el-button class="ms-left-buttion" size="small" @click="addAssertions">+{{$t('api_test.definition.request.assertions_rule')}}</el-button>
<br/>
<el-button class="ms-left-buttion" size="small" @click="addExtract">+{{$t('api_test.definition.request.extract_param')}}</el-button>
</el-col>
</el-row>
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
</div>
</template>
<script>
@ -113,7 +115,6 @@
import BatchAddParameter from "../../basis/BatchAddParameter";
import MsApiAdvancedConfig from "./ApiAdvancedConfig";
export default {
name: "MsApiHttpRequestForm",
components: {
@ -170,6 +171,7 @@
headerSuggestions: REQUEST_HEADERS,
isReloadData: false,
isBodyShow: true,
dialogVisible: false,
}
},
@ -204,7 +206,7 @@
this.reload();
},
copyRow(row) {
let obj =JSON.parse(JSON.stringify(row));
let obj = JSON.parse(JSON.stringify(row));
obj.id = getUUID();
this.request.hashTree.push(obj);
this.reload();

View File

@ -8,7 +8,7 @@
<ms-api-key-value :isShowEnable="false" :suggestions="headerSuggestions" :items="response.headers"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.response_body')" name="body" class="pane">
<ms-api-body :isShowEnable="false" :body="response.body" :headers="response.headers"/>
<ms-api-body :isReadOnly="false" :isShowEnable="false" :body="response.body" :headers="response.headers"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.status_code')" name="status_code" class="pane">

View File

@ -18,7 +18,7 @@ export const DEFAULT_DATA = [{
"name": "回收站",
"level": 1,
"children": [],
},{
}, {
"id": "root",
"name": "全部模块",
"level": 0,
@ -27,7 +27,20 @@ export const DEFAULT_DATA = [{
export const REQ_METHOD = [
{id: 'GET', label: 'GET'},
{id: 'POST', label: 'POST'}
{id: 'POST', label: 'POST'},
{id: 'PUT', label: 'PUT'},
{id: 'PATCH', label: 'PATCH'},
{id: 'DELETE', label: 'DELETE'},
{id: 'OPTIONS', label: 'OPTIONS'},
{id: 'HEAD', label: 'HEAD'},
{id: 'CONNECT', label: 'CONNECT'}
]
export const CASE_PRIORITY = [
{id: 'P0', label: 'P0'},
{id: 'P1', label: 'P1'},
{id: 'P2', label: 'P2'},
{id: 'P3', label: 'P3'}
]
export const API_STATUS = [

View File

@ -0,0 +1,127 @@
<template>
<ms-container>
<ms-main-container v-loading="result.loading">
<el-row :gutter="0"></el-row>
<el-row :gutter="0">
<el-col :span="4" >
<ms-api-info-card :api-count-data="apiCountData"/>
</el-col>
<el-col :span="4" :offset="2">
<ms-test-case-info-card :test-case-count-data="testCaseCountData"/>
</el-col>
<el-col :span="4" :offset="2">
<ms-scene-info-card :scene-count-data="sceneCountData"/>
</el-col>
<el-col :span="4" :offset="2">
<ms-schedule-task-info-card :schedule-task-count-data="scheduleTaskCountData"/>
</el-col>
</el-row>
<el-row>
<el-col :span="4" >
<ms-api-detail-card :api-count-data="apiCountData"/>
</el-col>
<el-col :span="4" :offset="2">
<ms-test-case-detail-card :test-case-count-data="testCaseCountData"/>
</el-col>
<el-col :span="4" :offset="2">
<ms-scene-detail-card :scene-count-data="sceneCountData"/>
</el-col>
<el-col :span="4" :offset="2">
<ms-schedule-task-detail-card :schedule-task-count-data="scheduleTaskCountData"/>
</el-col>
</el-row>
<el-row :gutter="20" >
<el-col :span="11" >
<ms-failure-test-case-list/>
</el-col>
<el-col :span="13" >
<ms-running-task-list/>
</el-col>
</el-row>
</ms-main-container>
</ms-container>
</template>
<script>
import MsContainer from "@/business/components/common/components/MsContainer";
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
import MsApiInfoCard from "./components/ApiInfoCard";
import MsSceneInfoCard from "./components/SceneInfoCard";
import MsScheduleTaskInfoCard from "./components/ScheduleTaskInfoCard";
import MsTestCaseInfoCard from "./components/TestCaseInfoCard";
import MsApiDetailCard from "./components/ApiDetailCard";
import MsSceneDetailCard from "./components/SceneDetailCard";
import MsScheduleTaskDetailCard from "./components/ScheduleTaskDetailCard";
import MsTestCaseDetailCard from "./components/TestCaseDetailCard";
import MsFailureTestCaseList from "./components/FailureTestCaseList";
import MsRunningTaskList from "./components/RunningTaskList"
import {getCurrentProjectID,getCurrentWorkspaceId} from "@/common/js/utils";
export default {
name: "ApiTestHome",
components: {
MsApiInfoCard, MsSceneInfoCard, MsScheduleTaskInfoCard, MsTestCaseInfoCard,
MsApiDetailCard, MsSceneDetailCard, MsScheduleTaskDetailCard, MsTestCaseDetailCard,
MsFailureTestCaseList,MsRunningTaskList,
MsMainContainer, MsContainer
},
data() {
return {
values: [],
apiCountData:{},
sceneCountData:{},
testCaseCountData:{},
scheduleTaskCountData:{},
result: {},
}
},
// activated() {
// this.getValues();
// },
// mounted() {
// this.getValues();
// },
created() {
this.search();
},
methods: {
search() {
let selectProjectId = getCurrentProjectID();
this.$get("/api/apiCount/"+selectProjectId, response => {
this.apiCountData = response.data;
});
this.$get("/api/testSceneInfoCount/"+selectProjectId, response => {
this.sceneCountData = response.data;
});
this.$get("/api/testCaseInfoCount/"+selectProjectId, response => {
this.testCaseCountData = response.data;
});
let workSpaceID = getCurrentWorkspaceId();
this.$get("/api/scheduleTaskInfoCount/"+workSpaceID, response => {
this.scheduleTaskCountData = response.data;
});
},
}
}
</script>
<style scoped>
.el-row {
margin-bottom: 20px;
margin-left: 20px;
margin-right: 20px;
}
</style>

View File

@ -0,0 +1,62 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.api_details_card.title')}}
</span>
</template>
<el-container>
<el-main>
<div class="text item">
{{$t('api_test.home_page.api_details_card.this_week_add',[apiCountData.thisWeekAddedCount])}}
</div>
</el-main>
</el-container>
</el-card>
</template>
<script>
export default {
name: "MsApiDetailCard",
components: {},
data() {
return {
result: {},
loading: false
}
},
props:{
apiCountData:{},
},
methods: {
search() {
// this.result = this.$get("/api/apiCount/5", response => {
// this.apiCountData = response.data;
// });
},
// link(row) {
// this.$router.push({
// path: '/api/report/view/' + row.id,
// })
// }
},
created() {
this.search();
},
activated() {
this.search();
}
}
</script>
<style>
.el-aside {
/*background-color: #D3DCE6;*/
/*color: #333;*/
text-align: center;
/*line-height: 20;*/
}
</style>

View File

@ -0,0 +1,63 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.api_count_card.title')}}
</span>
</template>
<el-container>
<el-aside width="40%">
<span class="countNumber">
{{apiCountData.allApiDataCountNumber}}
</span>
{{$t('api_test.home_page.unit_of_measurement')}}
</el-aside>
<el-container>
<el-main>
<div class="text item">
{{'HTTP: '+apiCountData.httpApiDataCountNumber}}
</div>
<div class="text item">
{{'RPC:'+apiCountData.rpcApiDataCountNumber}}
</div>
<div class="text item">
{{'TCP:'+apiCountData.tcpApiDataCountNumber}}
</div>
<div class="text item">
{{'SQL:'+apiCountData.sqlApiDataCountNumber}}
</div>
</el-main>
</el-container>
</el-container>
</el-card>
</template>
<script>
export default {
name: "MsApiInfoCard",
components: {},
data() {
return {
result: {},
loading: false
}
},
props:{
apiCountData:{},
},
}
</script>
<style>
.el-aside {
/*background-color: #D3DCE6;*/
/*color: #333;*/
text-align: center;
/*line-height: 20;*/
}
.countNumber{
font-size: 40px;
}
</style>

View File

@ -0,0 +1,57 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.failed_case_list.title')}}
</span>
</template>
<el-table border :data="tableData" class="adjust-table table-content" height="300px">
<el-table-column prop="sortIndex" :label="$t('api_test.home_page.failed_case_list.table_coloum.index')" width="100" show-overflow-tooltip/>
<el-table-column prop="caseName" :label="$t('api_test.home_page.failed_case_list.table_coloum.case_name')" width="250" show-overflow-tooltip/>
<el-table-column prop="testPlan" :label="$t('api_test.home_page.failed_case_list.table_coloum.test_plan')" show-overflow-tooltip/>
<el-table-column prop="failureTimes" :label="$t('api_test.home_page.failed_case_list.table_coloum.failure_times')" width="100" show-overflow-tooltip/>
</el-table>
</el-card>
</template>
<script>
import {getCurrentProjectID} from "@/common/js/utils";
export default {
name: "MsFailureTestCaseList",
data() {
return {
result: {},
tableData: [],
loading: false
}
},
methods: {
search() {
let projectID = getCurrentProjectID();
this.result = this.$get("/api/faliureCaseAboutTestPlan/"+projectID+"/10", response => {
this.tableData = response.data;
});
},
},
created() {
this.search();
},
activated() {
this.search();
}
}
</script>
<style scoped>
.el-table {
cursor:pointer;
}
</style>

View File

@ -0,0 +1,79 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.running_task_list.title')}}
</span>
</template>
<el-table border :data="tableData" class="adjust-table table-content" height="300px">
<el-table-column prop="index" :label="$t('api_test.home_page.running_task_list.table_coloum.index')" width="80" show-overflow-tooltip/>
<el-table-column prop="scenario" :label="$t('api_test.home_page.running_task_list.table_coloum.scenario')" width="200" show-overflow-tooltip/>
<el-table-column prop="rule" :label="$t('api_test.home_page.running_task_list.table_coloum.run_rule')" width="120" show-overflow-tooltip/>
<el-table-column width="100" :label="$t('api_test.home_page.running_task_list.table_coloum.task_status')">
<template v-slot:default="scope">
<el-switch @click.stop.native v-model="scope.row.taskStatus" @change="updateTask(scope.row)"/>
</template>
</el-table-column>
<el-table-column width="200" :label="$t('api_test.home_page.running_task_list.table_coloum.next_execution_time')">
<template v-slot:default="scope">
<span>{{ scope.row.nextExecutionTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="creator" :label="$t('api_test.home_page.running_task_list.table_coloum.create_user')" width="150" show-overflow-tooltip/>
<el-table-column width="200" :label="$t('api_test.home_page.running_task_list.table_coloum.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script>
import {getCurrentProjectID,getCurrentWorkspaceId} from "@/common/js/utils";
export default {
name: "MsRunningTaskList",
data() {
return {
value: '100',
result: {},
tableData: [],
loading: false
}
},
methods: {
search() {
let workSpaceID = getCurrentWorkspaceId();
this.result = this.$get("/api/runningTask/"+workSpaceID, response => {
this.tableData = response.data;
});
},
updateTask(taskRow){
this.result = this.$post('/api/schedule/updateEnableByPrimyKey', taskRow, response => {
this.search();
});
}
},
created() {
this.search();
},
activated() {
this.search();
}
}
</script>
<style scoped>
.el-table {
cursor:pointer;
}
</style>

View File

@ -0,0 +1,52 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.test_scene_details_card.title')}}
</span>
</template>
<el-container>
<el-main>
<div class="text item">
{{$t('api_test.home_page.test_scene_details_card.this_week_add',[sceneCountData.thisWeekAddedCount])}}
</div>
<div class="text item">
{{$t('api_test.home_page.test_scene_details_card.this_week_execute',[sceneCountData.thisWeekExecutedCount])}}
</div>
<div class="text item">
{{$t('api_test.home_page.test_scene_details_card.executed',[sceneCountData.executedCount])}}
</div>
</el-main>
</el-container>
</el-card>
</template>
<script>
export default {
name: "MsSceneDetailCard",
components: {},
data() {
return {
result: {},
loading: false
}
},
methods: {
},
props:{
sceneCountData:{},
},
}
</script>
<style>
.el-aside {
/*background-color: #D3DCE6;*/
/*color: #333;*/
text-align: center;
line-height: 100px;
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.test_scene_count_card.title')}}
</span>
</template>
<el-container>
<el-aside width="40%">
<span class="countNumber">
{{sceneCountData.allApiDataCountNumber}}
</span>
{{$t('api_test.home_page.unit_of_measurement')}}
</el-aside>
<el-container>
<el-main>
<div class="text item">
</div>
<div class="text item">
</div>
<div class="text item">
</div>
<div class="text item">
</div>
</el-main>
</el-container>
</el-container>
</el-card>
</template>
<script>
export default {
name: "MsSceneInfoCard",
components: {},
data() {
return {
result: {},
loading: false
}
},
props:{
sceneCountData:{},
},
methods: {
},
}
</script>
<style>
.el-aside {
/*background-color: #D3DCE6;*/
/*color: #333;*/
text-align: center;
line-height: 100px;
}
.countNumber{
font-size: 40px;
}
</style>

View File

@ -0,0 +1,58 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.schedule_task_details_card.title')}}
</span>
</template>
<el-container>
<el-main>
<div class="text item">
{{$t('api_test.home_page.schedule_task_details_card.this_week_add',[scheduleTaskCountData.thisWeekAddedCount])}}
</div>
<div class="text item">
{{$t('api_test.home_page.schedule_task_details_card.this_week_execute',[scheduleTaskCountData.thisWeekExecutedCount])}}
</div>
<div class="text item">
{{$t('api_test.home_page.schedule_task_details_card.executed',[scheduleTaskCountData.executedCount])}}
</div>
</el-main>
</el-container>
</el-card>
</template>
<script>
export default {
name: "MsTestCaseDetailCard",
components: {},
data() {
return {
result: {},
loading: false
}
},
props:{
scheduleTaskCountData:{},
},
methods: {
},
created() {
},
activated() {
}
}
</script>
<style>
.el-aside {
/*background-color: #D3DCE6;*/
/*color: #333;*/
text-align: center;
/*line-height: 200px;*/
}
</style>

View File

@ -0,0 +1,72 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.schedule_task_count_card.title')}}
</span>
</template>
<el-container>
<el-aside width="40%">
<span class="countNumber">
{{scheduleTaskCountData.allApiDataCountNumber}}
</span>
{{$t('api_test.home_page.unit_of_measurement')}}
</el-aside>
<el-container>
<el-main>
<div class="text item">
</div>
<div class="text item">
</div>
<div class="text item">
</div>
<div class="text item">
</div>
</el-main>
</el-container>
</el-container>
</el-card>
</template>
<script>
export default {
name: "MsScheduleTaskInfoCard",
components: {},
data() {
return {
result: {},
apiCountData: {},
loading: false
}
},
props:{
scheduleTaskCountData:{},
},
methods: {
},
created() {
},
activated() {
}
}
</script>
<style>
.el-aside {
/*background-color: #D3DCE6;*/
/*color: #333;*/
text-align: center;
/*line-height: 200px;*/
}
.countNumber{
font-size: 40px;
}
</style>

View File

@ -0,0 +1,58 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.test_case_details_card.title')}}
</span>
</template>
<el-container>
<el-main>
<div class="text item">
{{$t('api_test.home_page.test_case_details_card.this_week_add',[testCaseCountData.thisWeekAddedCount])}}
</div>
<div class="text item">
{{$t('api_test.home_page.test_case_details_card.this_week_execute',[testCaseCountData.thisWeekExecutedCount])}}
</div>
<div class="text item">
{{$t('api_test.home_page.test_case_details_card.executed',[testCaseCountData.executedCount])}}
</div>
</el-main>
</el-container>
</el-card>
</template>
<script>
export default {
name: "MsTestCaseDetailCard",
components: {},
data() {
return {
result: {},
loading: false
}
},
props:{
testCaseCountData:{},
},
methods: {
},
created() {
},
activated() {
}
}
</script>
<style>
.el-aside {
/*background-color: #D3DCE6;*/
/*color: #333;*/
text-align: center;
/*line-height: 200px;*/
}
</style>

View File

@ -0,0 +1,71 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">
{{$t('api_test.home_page.test_case_count_card.title')}}
</span>
</template>
<el-container>
<el-aside width="40%">
<span class="countNumber">
{{testCaseCountData.allApiDataCountNumber}}
</span>
{{$t('api_test.home_page.unit_of_measurement')}}
</el-aside>
<el-container>
<el-main>
<div class="text item">
{{'HTTP: '+testCaseCountData.httpApiDataCountNumber}}
</div>
<div class="text item">
{{'RPC:'+testCaseCountData.rpcApiDataCountNumber}}
</div>
<div class="text item">
{{'TCP:'+testCaseCountData.tcpApiDataCountNumber}}
</div>
<div class="text item">
{{'SQL:'+testCaseCountData.sqlApiDataCountNumber}}
</div>
</el-main>
</el-container>
</el-container>
</el-card>
</template>
<script>
export default {
name: "MsTestCaseInfoCard",
components: {},
data() {
return {
result: {},
loading: false
}
},
props:{
testCaseCountData:{},
},
methods: {
},
created() {
},
activated() {
}
}
</script>
<style>
.el-aside {
/*background-color: #D3DCE6;*/
/*color: #333;*/
text-align: center;
/*line-height: 200px;*/
}
.countNumber{
font-size: 40px;
}
</style>

View File

@ -11,7 +11,8 @@ export default {
{
path: 'home',
name: 'fucHome',
component: () => import('@/business/components/api/home/ApiTestHome'),
// component: () => import('@/business/components/api/home/ApiTestHome'),
component: () => import('@/business/components/api/homepage/ApiTestHomePage'),
},
{
path: "test/:type",
@ -53,7 +54,7 @@ export default {
path: "automation/report",
name: "ApiReportList",
component: () => import('@/business/components/api/automation/report/ApiReportList'),
},
},
{
path: 'monitor/view',
name: 'ApiMonitor',

View File

@ -3,6 +3,7 @@
<ms-drag-move-bar :direction="dragBarDirection" @widthChange="widthChange" @heightChange="heightChange"/>
<div class="ms-drawer-header" >
<slot name="header"></slot>
<i class="el-icon-close" @click="close"/>
</div>
<div class="ms-drawer-body">
<slot></slot>
@ -131,6 +132,9 @@
if (this.w > document.body.clientWidth) {
this.w = document.body.clientWidth;
}
},
close() {
this.$emit('close')
}
}
}
@ -177,4 +181,16 @@
position: relative;
}
.el-icon-close {
position: absolute;
right: 10px;
top: 13px;
color: gray;
font-size: 20px;
}
.el-icon-close:hover {
color: red;
}
</style>

View File

@ -108,9 +108,10 @@ import {
if (response.data.workspaceId) {
localStorage.setItem("workspace_id", response.data.workspaceId);
}
localStorage.removeItem(PROJECT_ID)
this.$router.push('/');
window.location.reload();
localStorage.removeItem(PROJECT_ID);
this.$router.push('/').then(() => {
window.location.reload();
}).catch(err => err);
});
},
changeWs(data) {
@ -121,9 +122,10 @@ import {
this.$post("/user/switch/source/ws/" + workspaceId, {}, response => {
saveLocalStorage(response);
localStorage.setItem("workspace_id", workspaceId);
localStorage.removeItem(PROJECT_ID)
this.$router.push('/');
window.location.reload();
localStorage.removeItem(PROJECT_ID);
this.$router.push('/').then(() => {
window.location.reload();
}).catch(err => err);
})
}
}

View File

@ -36,11 +36,6 @@ export default {
currentProject: String
},
created() {
if (getCurrentUser().lastProjectId) {
localStorage.setItem(PROJECT_ID, getCurrentUser().lastProjectId);
}
},
mounted() {
this.init();
},
computed: {
@ -68,10 +63,24 @@ export default {
this.result = this.$get("/project/listAll", response => {
this.items = response.data;
this.searchArray = response.data;
if (!getCurrentProjectID() && this.items.length > 0) {
this.change(this.items[0].id);
let userLastProjectId = getCurrentUser().lastProjectId;
if (userLastProjectId) {
// id
if (this.searchArray.length > 0 && this.searchArray.map(p => p.id).indexOf(userLastProjectId) !== -1) {
localStorage.setItem(PROJECT_ID, userLastProjectId);
}
}
let projectId = getCurrentProjectID();
if (projectId) {
// projectId ;
if (this.searchArray.length > 0 && this.searchArray.map(p => p.id).indexOf(projectId) === -1) {
this.change(this.items[0].id);
}
} else {
if (this.items.length > 0) {
this.change(this.items[0].id);
}
}
this.changeProjectName(projectId);
})
}

View File

@ -0,0 +1,66 @@
<template>
<el-card v-loading="result.loading">
<template slot="header">
<span style="font-size: 15px; color: #1E90FF">{{ $t('test_track.review.comment') }}</span>
<i class="el-icon-refresh" @click="getComments()"
style="margin-left:10px;font-size: 14px; cursor: pointer"/>
</template>
<div class="comment-list" style="height: 500px; overflow: auto;">
<review-comment-item v-for="(comment,index) in comments"
:key="index"
:comment="comment"
@refresh="getComments()"/>
<div v-if="comments.length === 0" style="text-align: center">
<i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;">
<span style="font-size: 15px; color: #8a8b8d;">
{{ $t('test_track.comment.no_comment') }}
</span>
</i>
</div>
</div>
</el-card>
</template>
<script>
import ReviewCommentItem from "@/business/components/track/review/commom/ReviewCommentItem";
export default {
name: "CaseComment",
components: {ReviewCommentItem},
props: {
caseId: {
type: String,
default: ''
}
},
watch: {
caseId() {
this.comments = [];
this.getComments();
}
},
created() {
this.comments = [];
this.getComments();
},
data() {
return {
comments: [],
result: {}
}
},
methods: {
getComments() {
if (this.caseId) {
this.result = this.$get('/test/case/comment/list/' + this.caseId, res => {
this.comments = res.data;
})
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,255 +1,263 @@
<template>
<div>
<el-dialog :close-on-click-modal="false" class="case-dialog"
@close="close"
:title="operationType == 'edit' ? ( readOnly ? $t('test_track.case.view_case') : $t('test_track.case.edit_case')) : $t('test_track.case.create')"
:visible.sync="dialogFormVisible" width="88%">
<el-dialog :close-on-click-modal="false"
@close="close"
:title="operationType == 'edit' ? ( readOnly ? $t('test_track.case.view_case') : $t('test_track.case.edit_case')) : $t('test_track.case.create')"
:visible.sync="dialogFormVisible" width="65%">
<el-row :gutter="10">
<div>
<el-col :span="17">
<el-card>
<el-form :model="form" :rules="rules" ref="caseFrom" v-loading="result.loading">
<el-form :model="form" :rules="rules" ref="caseFrom" v-loading="result.loading">
<el-row>
<el-col :span="8" :offset="1">
<el-form-item
:placeholder="$t('test_track.case.input_name')"
:label="$t('test_track.case.name')"
:label-width="formLabelWidth"
prop="name">
<el-input class="case-name" :disabled="readOnly" v-model="form.name"></el-input>
</el-form-item>
</el-col>
<el-row>
<el-col :span="8" :offset="1">
<el-form-item
:placeholder="$t('test_track.case.input_name')"
:label="$t('test_track.case.name')"
:label-width="formLabelWidth"
prop="name">
<el-input class="case-name" :disabled="readOnly" v-model="form.name"></el-input>
</el-form-item>
</el-col>
<el-col :span="11" :offset="2">
<el-form-item :label="$t('test_track.case.module')" :label-width="formLabelWidth" prop="module">
<el-select
v-model="form.module"
:disabled="readOnly"
:placeholder="$t('test_track.case.input_module')"
filterable>
<el-option
v-for="item in moduleOptions"
:key="item.id"
:label="item.path"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-col :span="11" :offset="2">
<el-form-item :label="$t('test_track.case.module')" :label-width="formLabelWidth" prop="module">
<el-select
v-model="form.module"
:disabled="readOnly"
:placeholder="$t('test_track.case.input_module')"
filterable>
<el-option
v-for="item in moduleOptions"
:key="item.id"
:label="item.path"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="10" :offset="1">
<el-form-item :label="$t('test_track.case.maintainer')" :label-width="formLabelWidth" prop="maintainer">
<el-select :disabled="readOnly" v-model="form.maintainer"
:placeholder="$t('test_track.case.input_maintainer')" filterable>
<el-option
v-for="item in maintainerOptions"
:key="item.id"
:label="item.id + ' (' + item.name + ')'"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('test_track.case.priority')" :label-width="formLabelWidth" prop="priority">
<el-select :disabled="readOnly" v-model="form.priority" clearable
:placeholder="$t('test_track.case.input_priority')">
<el-option label="P0" value="P0"></el-option>
<el-option label="P1" value="P1"></el-option>
<el-option label="P2" value="P2"></el-option>
<el-option label="P3" value="P3"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="10" :offset="1">
<el-form-item :label="$t('test_track.case.maintainer')" :label-width="formLabelWidth" prop="maintainer">
<el-select :disabled="readOnly" v-model="form.maintainer"
:placeholder="$t('test_track.case.input_maintainer')" filterable>
<el-option
v-for="item in maintainerOptions"
:key="item.id"
:label="item.id + ' (' + item.name + ')'"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('test_track.case.priority')" :label-width="formLabelWidth" prop="priority">
<el-select :disabled="readOnly" v-model="form.priority" clearable
:placeholder="$t('test_track.case.input_priority')">
<el-option label="P0" value="P0"></el-option>
<el-option label="P1" value="P1"></el-option>
<el-option label="P2" value="P2"></el-option>
<el-option label="P3" value="P3"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="10" :offset="1">
<el-form-item :label="$t('test_track.case.type')" :label-width="formLabelWidth" prop="type">
<el-select @change="typeChange" :disabled="readOnly" v-model="form.type"
:placeholder="$t('test_track.case.input_type')">
<el-option :label="$t('commons.functional')" value="functional"></el-option>
<el-option :label="$t('commons.performance')" value="performance"></el-option>
<el-option :label="$t('commons.api')" value="api"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('test_track.case.method')" :label-width="formLabelWidth" prop="method">
<el-select :disabled="readOnly" v-model="form.method" :placeholder="$t('test_track.case.input_method')">
<el-option
v-for="item in methodOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="10" :offset="1">
<el-form-item :label="$t('test_track.case.type')" :label-width="formLabelWidth" prop="type">
<el-select @change="typeChange" :disabled="readOnly" v-model="form.type"
:placeholder="$t('test_track.case.input_type')">
<el-option :label="$t('commons.functional')" value="functional"></el-option>
<el-option :label="$t('commons.performance')" value="performance"></el-option>
<el-option :label="$t('commons.api')" value="api"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('test_track.case.method')" :label-width="formLabelWidth" prop="method">
<el-select :disabled="readOnly" v-model="form.method" :placeholder="$t('test_track.case.input_method')">
<el-option
v-for="item in methodOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="form.method && form.method == 'auto'">
<el-col :span="9" :offset="1">
<el-form-item :label="$t('test_track.case.relate_test')" :label-width="formLabelWidth" prop="testId">
<el-select filterable :disabled="readOnly" v-model="form.testId"
:placeholder="$t('test_track.case.input_type')">
<el-option
v-for="item in testOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="9" :offset="1" v-if="form.testId=='other'">
<el-form-item :label="$t('test_track.case.test_name')" :label-width="formLabelWidth" prop="testId">
<el-input v-model="form.otherTestName" :placeholder="$t('test_track.case.input_test_case')"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row style="margin-top: 15px;">
<el-col :offset="2">{{ $t('test_track.case.prerequisite') }}:</el-col>
</el-row>
<el-row type="flex" justify="center" style="margin-top: 10px;">
<el-col :span="20">
<el-form-item prop="prerequisite">
<el-input :disabled="readOnly" v-model="form.prerequisite"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:rows="2"
:placeholder="$t('test_track.case.input_prerequisite')"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="form.method && form.method == 'auto'">
<el-col :span="9" :offset="1">
<el-form-item :label="$t('test_track.case.relate_test')" :label-width="formLabelWidth" prop="testId">
<el-select filterable :disabled="readOnly" v-model="form.testId"
:placeholder="$t('test_track.case.input_type')">
<el-option
v-for="item in testOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="9" :offset="1" v-if="form.testId=='other'">
<el-form-item :label="$t('test_track.case.test_name')" :label-width="formLabelWidth" prop="testId">
<el-input v-model="form.otherTestName" :placeholder="$t('test_track.case.input_test_case')"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row style="margin-top: 15px;">
<el-col :offset="2">{{ $t('test_track.case.prerequisite') }}:</el-col>
</el-row>
<el-row type="flex" justify="center" style="margin-top: 10px;">
<el-col :span="20">
<el-form-item prop="prerequisite">
<el-input :disabled="readOnly" v-model="form.prerequisite"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:rows="2"
:placeholder="$t('test_track.case.input_prerequisite')"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="form.method && form.method != 'auto'" style="margin-bottom: 10px">
<el-col :offset="2">{{ $t('test_track.case.steps') }}:</el-col>
</el-row>
<el-row v-if="form.method && form.method != 'auto'" style="margin-bottom: 10px">
<el-col :offset="2">{{ $t('test_track.case.steps') }}:</el-col>
</el-row>
<el-row v-if="form.method && form.method != 'auto'" type="flex" justify="center">
<el-col :span="20">
<el-table
v-if="isStepTableAlive"
:data="form.steps"
class="tb-edit"
border
size="mini"
:default-sort="{prop: 'num', order: 'ascending'}"
highlight-current-row>
<el-table-column :label="$t('test_track.case.number')" prop="num" min-width="10%"></el-table-column>
<el-table-column :label="$t('test_track.case.step_desc')" prop="desc" min-width="35%">
<template v-slot:default="scope">
<el-input
class="table-edit-input"
<el-row v-if="form.method && form.method != 'auto'" type="flex" justify="center">
<el-col :span="20">
<el-table
v-if="isStepTableAlive"
:data="form.steps"
class="tb-edit"
border
size="mini"
:disabled="readOnly"
type="textarea"
:autosize="{ minRows: 1, maxRows: 6}"
:rows="2"
v-model="scope.row.desc"
:placeholder="$t('commons.input_content')"
clearable/>
</template>
</el-table-column>
<el-table-column :label="$t('test_track.case.expected_results')" prop="result" min-width="35%">
<template v-slot:default="scope">
<el-input
class="table-edit-input"
size="mini"
:disabled="readOnly"
type="textarea"
:autosize="{ minRows: 1, maxRows: 6}"
:rows="2"
v-model="scope.row.result"
:placeholder="$t('commons.input_content')"
clearable/>
</template>
</el-table-column>
<el-table-column :label="$t('commons.input_content')" min-width="20%">
<template v-slot:default="scope">
<el-button
type="primary"
:disabled="readOnly"
icon="el-icon-plus"
circle size="mini"
@click="handleAddStep(scope.$index, scope.row)"></el-button>
<el-button
icon="el-icon-document-copy"
type="success"
:disabled="readOnly"
circle size="mini"
@click="handleCopyStep(scope.$index, scope.row)"></el-button>
<el-button
type="danger"
icon="el-icon-delete"
circle size="mini"
@click="handleDeleteStep(scope.$index, scope.row)"
:disabled="readOnly || (scope.$index == 0 && form.steps.length <= 1)"></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
:default-sort="{prop: 'num', order: 'ascending'}"
highlight-current-row>
<el-table-column :label="$t('test_track.case.number')" prop="num" min-width="10%"></el-table-column>
<el-table-column :label="$t('test_track.case.step_desc')" prop="desc" min-width="35%">
<template v-slot:default="scope">
<el-input
class="table-edit-input"
size="mini"
:disabled="readOnly"
type="textarea"
:autosize="{ minRows: 1, maxRows: 6}"
:rows="2"
v-model="scope.row.desc"
:placeholder="$t('commons.input_content')"
clearable/>
</template>
</el-table-column>
<el-table-column :label="$t('test_track.case.expected_results')" prop="result" min-width="35%">
<template v-slot:default="scope">
<el-input
class="table-edit-input"
size="mini"
:disabled="readOnly"
type="textarea"
:autosize="{ minRows: 1, maxRows: 6}"
:rows="2"
v-model="scope.row.result"
:placeholder="$t('commons.input_content')"
clearable/>
</template>
</el-table-column>
<el-table-column :label="$t('commons.input_content')" min-width="20%">
<template v-slot:default="scope">
<el-button
type="primary"
:disabled="readOnly"
icon="el-icon-plus"
circle size="mini"
@click="handleAddStep(scope.$index, scope.row)"></el-button>
<el-button
icon="el-icon-document-copy"
type="success"
:disabled="readOnly"
circle size="mini"
@click="handleCopyStep(scope.$index, scope.row)"></el-button>
<el-button
type="danger"
icon="el-icon-delete"
circle size="mini"
@click="handleDeleteStep(scope.$index, scope.row)"
:disabled="readOnly || (scope.$index == 0 && form.steps.length <= 1)"></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-row style="margin-top: 15px;margin-bottom: 10px">
<el-col :offset="2">{{ $t('commons.remark') }}:</el-col>
</el-row>
<el-row type="flex" justify="center">
<el-col :span="20">
<el-form-item prop="remark">
<el-input v-model="form.remark"
:autosize="{ minRows: 2, maxRows: 4}"
type="textarea"
:disabled="readOnly"
:rows="2"
:placeholder="$t('commons.input_content')"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row style="margin-top: 15px;margin-bottom: 10px">
<el-col :offset="2">{{ $t('commons.remark') }}:</el-col>
</el-row>
<el-row type="flex" justify="center">
<el-col :span="20">
<el-form-item prop="remark">
<el-input v-model="form.remark"
:autosize="{ minRows: 2, maxRows: 4}"
type="textarea"
:disabled="readOnly"
:rows="2"
:placeholder="$t('commons.input_content')"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row style="margin-top: 15px;margin-bottom: 10px">
<el-col :offset="2" :span="20">{{ $t('test_track.case.attachment') }}:
<el-upload
accept=".jpg,.jpeg,.png,.xlsx,.doc,.pdf,.docx"
action=""
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="handleUpload"
:on-exceed="handleExceed"
multiple
:limit="8"
:file-list="fileList">
<el-button icon="el-icon-plus" size="mini"></el-button>
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
</el-upload>
</el-col>
<el-col :offset="2" :span="20">
<test-case-attachment :table-data="tableData"
:read-only="readOnly"
:is-delete="true"
@handleDelete="handleDelete"
/>
</el-col>
</el-row>
<el-row style="margin-top: 15px;margin-bottom: 10px">
<el-col :offset="2" :span="20">{{ $t('test_track.case.attachment') }}:
<el-upload
accept=".jpg,.jpeg,.png,.xlsx,.doc,.pdf,.docx"
action=""
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="handleUpload"
:on-exceed="handleExceed"
multiple
:limit="8"
:file-list="fileList">
<el-button icon="el-icon-plus" size="mini"></el-button>
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
</el-upload>
</el-col>
<el-col :offset="2" :span="20">
<test-case-attachment :table-data="tableData"
:read-only="readOnly"
:is-delete="true"
@handleDelete="handleDelete"
/>
</el-col>
</el-row>
</el-form>
<el-row style="float: right; margin-bottom: 10px;">
<el-switch v-if="operationType == 'add'"
v-model="isCreateContinue"
:active-text="$t('test_track.case.save_create_continue')">
</el-switch>
<ms-dialog-footer v-if="!readOnly"
@cancel="dialogFormVisible = false"
@confirm="saveCase"/>
</el-row>
<template v-slot:footer>
<el-switch v-if="operationType == 'add'"
v-model="isCreateContinue"
:active-text="$t('test_track.case.save_create_continue')">
</el-switch>
<ms-dialog-footer v-if="!readOnly"
@cancel="dialogFormVisible = false"
@confirm="saveCase"/>
</template>
</el-form>
</el-dialog>
</el-card>
</el-col>
<el-col :span="7">
<case-comment :case-id="testCase ? testCase.id : ''"/>
</el-col>
</div>
</el-row>
</div>
</el-dialog>
</template>
@ -264,10 +272,11 @@ import {Message} from "element-ui";
import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment";
import {getCurrentProjectID} from "../../../../../common/js/utils";
import {buildNodePath} from "../../../api/definition/model/NodeTree";
import CaseComment from "@/business/components/track/case/components/CaseComment";
export default {
name: "TestCaseEdit",
components: {MsDialogFooter, TestCaseAttachment},
components: {CaseComment, MsDialogFooter, TestCaseAttachment},
data() {
return {
result: {},
@ -318,7 +327,8 @@ export default {
methodOptions: [
{value: 'auto', label: this.$t('test_track.case.auto')},
{value: 'manual', label: this.$t('test_track.case.manual')}
]
],
testCase: {}
};
},
props: {
@ -347,6 +357,11 @@ export default {
this.$nextTick(() => (this.isStepTableAlive = true));
},
open(testCase) {
this.testCase = {};
if (testCase) {
//
this.testCase = testCase.isCopy ? {} : testCase;
}
this.resetForm();
this.projectId = getCurrentProjectID();
if (window.history && window.history.pushState) {
@ -697,4 +712,8 @@ export default {
width: 194px;
}
.case-dialog >>> .el-dialog__body {
padding: 0 20px 10px 20px;
}
</style>

View File

@ -7,62 +7,40 @@
ref="baseRelevance">
<template v-slot:aside>
<node-tree class="node-tree"
@nodeSelectEvent="nodeChange"
@refresh="refresh"
:tree-nodes="treeNodes"
ref="nodeTree"/>
<!--<node-tree class="node-tree"-->
<!--@nodeSelectEvent="nodeChange"-->
<!--@refresh="refresh"-->
<!--:tree-nodes="treeNodes"-->
<!--ref="nodeTree"/>-->
<ms-api-module
@nodeSelectEvent="nodeChange"
@protocolChange="handleProtocolChange"
@refreshTable="refresh"
@exportAPI="exportAPI"
@debug="debug"
@saveAsEdit="editApi"
@setModuleOptions="setModuleOptions"
@enableTrash="enableTrash"
:is-read-only="true"
:type="'edit'"
ref="nodeTree"/>
</template>
<ms-table-header :condition.sync="condition" @search="search" title="" :show-create="false"/>
<el-table
v-loading="result.loading"
:data="testCases"
@filter-change="filter"
row-key="id"
@mouseleave.passive="leave"
v-el-table-infinite-scroll="scrollLoading"
@select-all="handleSelectAll"
@select="handleSelectionChange"
height="50vh"
ref="table">
<api-list
v-if="isApiListEnable"
:current-protocol="currentProtocol"
:currentRow="currentRow"
:select-node-ids="selectNodeIds"
:trash-enable="trashEnable"
:is-api-list-enable="isApiListEnable"
@editApi="editApi"
@handleCase="handleCase"
@showExecResult="showExecResult"
@isApiListEnableChange="isApiListEnableChange"
ref="apiList"/>
<el-table-column
type="selection"></el-table-column>
<el-table-column
prop="name"
:label="$t('test_track.case.name')"
style="width: 100%">
<template v-slot:default="scope">
{{scope.row.name}}
</template>
</el-table-column>
<el-table-column
prop="priority"
:filters="priorityFilters"
column-key="priority"
:label="$t('test_track.case.priority')"
show-overflow-tooltip>
<template v-slot:default="scope">
<priority-table-item :value="scope.row.priority"/>
</template>
</el-table-column>
<el-table-column
prop="type"
:filters="typeFilters"
column-key="type"
:label="$t('test_track.case.type')"
show-overflow-tooltip>
<template v-slot:default="scope">
<type-table-item :value="scope.row.type"/>
</template>
</el-table-column>
</el-table>
<div v-if="!lineStatus" style="text-align: center">{{$t('test_track.review_view.last_page')}}</div>
<div style="text-align: center"> {{total}} </div>
</test-case-relevance-base>
@ -76,14 +54,17 @@
import MsTableSearchBar from "../../../../../common/components/MsTableSearchBar";
import MsTableAdvSearchBar from "../../../../../common/components/search/MsTableAdvSearchBar";
import MsTableHeader from "../../../../../common/components/MsTableHeader";
import {TEST_CASE_CONFIGS} from "../../../../../common/components/search/search-components";
import elTableInfiniteScroll from 'el-table-infinite-scroll';
import TestCaseRelevanceBase from "../base/TestCaseRelevanceBase";
import {_filter} from "../../../../../../../common/js/utils";
import ApiList from "../../../../../api/automation/scenario/api/ApiList";
import MsApiModule from "../../../../../api/definition/components/module/ApiModule";
import {getCurrentProjectID} from "../../../../../../../common/js/utils";
export default {
name: "TestCaseApiRelevance",
components: {
MsApiModule,
ApiList,
TestCaseRelevanceBase,
NodeTree,
PriorityTableItem,
@ -97,34 +78,17 @@
},
data() {
return {
result: {},
isCheckAll: false,
testCases: [],
selectIds: new Set(),
treeNodes: [],
showCasePage: true,
apiDefaultTab: 'default',
currentProtocol: null,
currentModule: null,
selectNodeIds: [],
selectNodeNames: [],
projectId: '',
projectName: '',
projects: [],
pageSize: 50,
currentPage: 1,
total: 0,
lineStatus: true,
condition: {
components: TEST_CASE_CONFIGS
},
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
typeFilters: [
{text: this.$t('commons.functional'), value: 'functional'},
{text: this.$t('commons.performance'), value: 'performance'},
{text: this.$t('commons.api'), value: 'api'}
]
currentApi: {},
moduleOptions: {},
trashEnable: false,
isApiListEnable: true,
condition: {},
currentRow: {}
};
},
props: {
@ -134,15 +98,12 @@
},
watch: {
planId() {
this.condition.planId = this.planId;
},
selectNodeIds() {
this.search();
// this.condition.planId = this.planId;
},
projectId() {
this.condition.projectId = this.projectId;
this.getProjectNode();
this.search();
// this.getProjectNode();
// this.search();
}
},
updated() {
@ -153,11 +114,96 @@
open() {
this.$refs.baseRelevance.open();
},
search() {
},
setProject(projectId) {
this.projectId = projectId;
},
isApiListEnableChange(data) {
this.isApiListEnable = data;
},
handleCommand(e) {
switch (e) {
case "ADD":
this.handleTabAdd(e);
break;
case "TEST":
this.handleTabsEdit(this.$t("commons.api"), e);
break;
case "CLOSE_ALL":
this.handleTabClose();
break;
default:
this.handleTabsEdit(this.$t('api_test.definition.request.fast_debug'), "debug");
break;
}
},
debug(id) {
this.handleTabsEdit(this.$t('api_test.definition.request.fast_debug'), "debug", id);
},
editApi(row) {
let name = this.$t('api_test.definition.request.edit_api');
if (row.name) {
name = this.$t('api_test.definition.request.edit_api') + "-" + row.name;
}
// this.handleTabsEdit(name, "ADD", row);
},
handleCase(api) {
this.currentApi = api;
this.showCasePage = false;
},
apiCaseClose() {
this.showCasePage = true;
},
exportAPI() {
if (!this.$refs.apiList[0].tableData) {
return;
}
let obj = {projectName: getCurrentProjectID(), protocol: this.currentProtocol, data: this.$refs.apiList[0].tableData}
// downloadFile("API.json", JSON.stringify(obj));
},
refresh(data) {
this.$refs.apiList[0].initTable(data);
},
setTabTitle(data) {
for (let index in this.apiTabs) {
let tab = this.apiTabs[index];
if (tab.name === this.apiDefaultTab) {
tab.title = this.$t('api_test.definition.request.edit_api') + "-" + data.name;
break;
}
}
this.runTestData = data;
},
runTest(data) {
this.setTabTitle(data);
this.handleCommand("TEST");
},
saveApi(data) {
this.setTabTitle(data);
this.$refs.apiList[0].initApiTable(data);
},
showExecResult(row){
this.debug(row);
},
nodeChange(node, nodeIds, pNodes) {
this.selectNodeIds = nodeIds;
},
handleProtocolChange(protocol) {
this.currentProtocol = protocol;
},
setModuleOptions(data) {
this.moduleOptions = data;
},
enableTrash(data) {
this.trashEnable = data;
},
saveCaseRelevance() {
let param = {};
param.planId = this.planId;
@ -176,148 +222,6 @@
this.$emit('refresh');
});
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
search() {
this.currentPage = 1;
this.testCases = [];
this.getTestCases(true);
},
getTestCases(flag) {
if (this.planId) {
this.condition.planId = this.planId;
}
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
this.condition.nodeIds = this.selectNodeIds;
} else {
this.condition.nodeIds = [];
}
if (this.projectId) {
this.condition.projectId = this.projectId;
// this.result = this.$post(this.buildPagePath('/test/case/name'), this.condition, response => {
// let data = response.data;
// this.total = data.itemCount;
// let tableData = data.listObject;
// tableData.forEach(item => {
// item.checked = false;
// });
// flag ? this.testCases = tableData : this.testCases = this.testCases.concat(tableData);
// //
// let hash = {}
// this.testCases = this.testCases.reduce((item, next) => {
// if (!hash[next.id]) {
// hash[next.id] = true
// item.push(next)
// }
// return item
// }, [])
//
// this.lineStatus = tableData.length === 50 && this.testCases.length < this.total;
// });
this.result = this.$post('/api/definition/list/1/10', this.condition, response => {
let data = response.data;
this.total = data.itemCount;
let tableData = data.listObject;
tableData.forEach(item => {
item.checked = false;
});
flag ? this.testCases = tableData : this.testCases = this.testCases.concat(tableData);
//
let hash = {}
this.testCases = this.testCases.reduce((item, next) => {
if (!hash[next.id]) {
hash[next.id] = true
item.push(next)
}
return item
}, [])
this.lineStatus = tableData.length === 50 && this.testCases.length < this.total;
});
}
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.testCases.forEach(item => {
this.selectIds.add(item.id);
});
} else {
this.testCases.forEach(item => {
if (this.selectIds.has(item.id)) {
this.selectIds.delete(item.id);
}
});
}
},
handleSelectionChange(selection, row) {
if (this.selectIds.has(row.id)) {
this.selectIds.delete(row.id);
} else {
this.selectIds.add(row.id);
}
},
nodeChange(nodeIds, nodeNames) {
this.selectNodeIds = nodeIds;
this.selectNodeNames = nodeNames;
},
refresh() {
this.close();
},
scrollLoading() {
if (this.lineStatus) {
this.currentPage += 1;
this.getTestCases();
}
},
getAllNodeTreeByPlanId() {
if (this.planId) {
let param = {
testPlanId: this.planId,
projectId: this.projectId
};
this.result = this.$get('/api/module/list/' + this.project + '/HTTP', response => {
this.treeNodes = response.data;
});
}
},
close() {
this.lineStatus = false;
this.selectIds.clear();
this.selectNodeIds = [];
this.selectNodeNames = [];
},
filter(filters) {
_filter(filters, this.condition);
this.search();
},
toggleSelection(rows) {
rows.forEach(row => {
this.selectIds.forEach(id => {
if (row.id === id) {
// true
this.$refs.table.toggleRowSelection(row, true)
}
})
})
},
getProjectNode(projectId) {
const index = this.projects.findIndex(project => project.id === projectId);
if (index !== -1) {
this.projectName = this.projects[index].name;
}
if (projectId) {
this.projectId = projectId;
}
this.$refs.nodeTree.result = this.$get('/api/module/list/' + this.projectId + '/HTTP', response => {
this.treeNodes = response.data;
});
// this.$refs.nodeTree.result = this.$post('/api/module/list/' + this.project + '/HTTP',
// {testPlanId: this.planId, projectId: this.projectId}, response => {
// this.treeNodes = response.data;
// });
this.selectNodeIds = [];
}
}
}
</script>

View File

@ -11,9 +11,9 @@
<el-aside class="tree-aside" width="250px">
<select-menu
:data="projects"
width="185px"
width="160px"
:current-data="currentProject"
:title="$t('test_track.plan_view.plan')"
:title="$t('test_track.switch_project')"
@dataChange="changeProject"/>
<slot name="aside"></slot>
</el-aside>
@ -79,11 +79,6 @@
this.$emit('save');
},
refresh() {
// this.close();
},
close() {
this.dialogVisible = false;
},

@ -1 +1 @@
Subproject commit d39dafaf84b9c7a56cb51f2caf67dd7dfde5938c
Subproject commit a22a3005d9bd254793fcf634d72539cbdf31be3a

View File

@ -791,6 +791,64 @@ export default {
swagger_export_tip: "Export jSON-formatted files via Swagger website",
suffixFormatErr: "The file format does not meet the requirements",
swagger_url_import: "Import using URL",
},
home_page:{
unit_of_measurement:"",
api_count_card:{
title: "API count",
},
test_case_count_card:{
title: "Test case count",
},
test_scene_count_card:{
title: "Scene count",
},
schedule_task_count_card:{
title: "Schedule task count",
},
api_details_card:{
title: "API",
this_week_add:"Added {0} this week",
},
test_case_details_card:{
title: "Test case",
this_week_add:"Added {0} this week",
this_week_execute:"Executed {0} this week",
executed:"Executed {0} in history",
},
test_scene_details_card:{
title: "Scene",
this_week_add:"Added {0} this week",
this_week_execute:"Executed {0} this week",
executed:"Executed {0} in history",
},
schedule_task_details_card:{
title: "Schedule task",
this_week_add:"Added {0} this week",
this_week_execute:"Executed {0} this week",
executed:"Executed {0} in history",
},
failed_case_list:{
title: "Top 10 failure process set cases in the past 7 days",
table_coloum:{
index: "Ranking",
case_name: "Case name",
test_plan: "Test plan",
failure_times: "Failure times",
},
},
running_task_list:{
title: "Running task",
table_coloum:{
index: "Index",
scenario: "Scene",
run_rule: "Rule",
task_status: "Status",
next_execution_time: "Next Execution Time",
create_user: "Creator",
update_time: "Update time",
},
}
}
},
api_report: {

View File

@ -792,6 +792,64 @@ export default {
swagger_export_tip: "通过 Swagger 页面导出",
suffixFormatErr: "文件格式不符合要求",
swagger_url_import: "使用URL导入",
},
home_page:{
unit_of_measurement:"个",
api_count_card:{
title: "接口总数",
},
test_case_count_card:{
title: "用例总数",
},
test_scene_count_card:{
title: "场景总数",
},
schedule_task_count_card:{
title: "定时任务总数",
},
api_details_card:{
title: "接口",
this_week_add:"本周新增{0}个",
},
test_case_details_card:{
title: "用例",
this_week_add:"本周新增: {0}个",
this_week_execute:"本周执行: {0}次",
executed:"历史总执行: {0}次",
},
test_scene_details_card:{
title: "场景",
this_week_add:"本周新增: {0}个",
this_week_execute:"本周执行: {0}次",
executed:"历史总执行: {0}次",
},
schedule_task_details_card:{
title: "定时任务",
this_week_add:"本周新增: {0}个",
this_week_execute:"本周执行: {0}次",
executed:"历史总执行: {0}次",
},
failed_case_list:{
title: "过去7天流程集失败用例TOP 10",
table_coloum:{
index: "排名",
case_name: "用例名称",
test_plan: "所属测试计划",
failure_times: "失败次数",
},
},
running_task_list:{
title: "运行中的任务",
table_coloum:{
index: "序号",
scenario: "场景名称",
run_rule: "运行规则",
task_status: "任务状态",
next_execution_time: "下次执行时间",
create_user: "创建人",
update_time: "更新时间",
},
}
}
},
api_report: {

View File

@ -791,6 +791,64 @@ export default {
swagger_export_tip: "通過 Swagger 頁面導出",
suffixFormatErr: "文件格式不符合要求",
swagger_url_import: "使用URL導入",
},
home_page:{
unit_of_measurement:"個",
api_count_card:{
title: "接口總數",
},
test_case_count_card:{
title: "用例總數",
},
test_scene_count_card:{
title: "場景總數",
},
schedule_task_count_card:{
title: "定時任務總數",
},
api_details_card:{
title: "接口",
this_week_add:"本週新增{0}个",
},
test_case_details_card:{
title: "用例",
this_week_add:"本週新增: {0}个",
this_week_execute:"本週執行: {0}次",
executed:"歷史總執行: {0}次",
},
test_scene_details_card:{
title: "場景",
this_week_add:"本週新增: {0}个",
this_week_execute:"本週執行: {0}次",
executed:"歷史總執行: {0}次",
},
schedule_task_details_card:{
title: "定時任務",
this_week_add:"本週新增: {0}个",
this_week_execute:"本週執行: {0}次",
executed:"歷史總執行: {0}次",
},
failed_case_list:{
title: "過去7天流程集失敗用例TOP 10",
table_coloum:{
index: "排名",
case_name: "用例名稱",
test_plan: "所屬測試計畫",
failure_times: "失敗次數",
},
},
running_task_list:{
title: "運行中的任務",
table_coloum:{
index: "序號",
scenario: "場景名稱",
run_rule: "運行規則",
task_status: "任務狀態",
next_execution_time: "下次執行時間",
create_user: "創建人",
update_time: "更新時間",
},
}
}
},
api_report: {