This commit is contained in:
chenjianxing 2020-05-22 09:25:39 +08:00
commit cf83d9efae
22 changed files with 306 additions and 130 deletions

View File

@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
@RestController @RestController
@ -64,7 +65,7 @@ public class APITestController {
} }
@PostMapping(value = "/run") @PostMapping(value = "/run")
public void run(@RequestBody SaveAPITestRequest request) { public String run(@RequestBody SaveAPITestRequest request) {
apiTestService.run(request); return apiTestService.run(request);
} }
} }

View File

@ -61,7 +61,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
queue.forEach((id, sampleResults) -> { queue.forEach((id, sampleResults) -> {
TestResult testResult = new TestResult(); TestResult testResult = new TestResult();
testResult.setId(id); testResult.setTestId(id);
testResult.setTotal(sampleResults.size()); testResult.setTotal(sampleResults.size());
// key: 场景Id // key: 场景Id
@ -104,7 +104,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
testResult.getScenarios().addAll(scenarios.values()); testResult.getScenarios().addAll(scenarios.values());
testResult.getScenarios().sort(Comparator.comparing(ScenarioResult::getOrder)); testResult.getScenarios().sort(Comparator.comparing(ScenarioResult::getOrder));
apiTestService.changeStatus(id, APITestStatus.Completed); apiTestService.changeStatus(id, APITestStatus.Completed);
apiReportService.save(testResult); apiReportService.complete(testResult);
}); });
queue.clear(); queue.clear();
super.teardownTest(context); super.teardownTest(context);

View File

@ -8,7 +8,7 @@ import java.util.List;
@Data @Data
public class TestResult { public class TestResult {
private String id; private String testId;
private int success = 0; private int success = 0;

View File

@ -5,12 +5,15 @@ import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.DeleteAPIReportRequest; import io.metersphere.api.dto.DeleteAPIReportRequest;
import io.metersphere.api.dto.QueryAPIReportRequest; import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.jmeter.TestResult; import io.metersphere.api.jmeter.TestResult;
import io.metersphere.base.domain.ApiTest;
import io.metersphere.base.domain.ApiTestReport; import io.metersphere.base.domain.ApiTestReport;
import io.metersphere.base.domain.ApiTestWithBLOBs; import io.metersphere.base.domain.ApiTestReportExample;
import io.metersphere.base.mapper.ApiTestReportMapper; import io.metersphere.base.mapper.ApiTestReportMapper;
import io.metersphere.base.mapper.ext.ExtApiTestReportMapper; import io.metersphere.base.mapper.ext.ExtApiTestReportMapper;
import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.dto.DashboardTestDTO; import io.metersphere.dto.DashboardTestDTO;
import io.metersphere.i18n.Translator;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -25,8 +28,6 @@ import javax.annotation.Resource;
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class APIReportService { public class APIReportService {
@Resource
private APITestService apiTestService;
@Resource @Resource
private ApiTestReportMapper apiTestReportMapper; private ApiTestReportMapper apiTestReportMapper;
@Resource @Resource
@ -52,24 +53,58 @@ public class APIReportService {
apiTestReportMapper.deleteByPrimaryKey(request.getId()); apiTestReportMapper.deleteByPrimaryKey(request.getId());
} }
public void save(TestResult result) { public void deleteByTestId(String testId) {
ApiTestWithBLOBs test = apiTestService.get(result.getId()); ApiTestReportExample example = new ApiTestReportExample();
ApiTestReport report = new ApiTestReport(); example.createCriteria().andTestIdEqualTo(testId);
report.setId(UUID.randomUUID().toString()); apiTestReportMapper.deleteByExample(example);
report.setTestId(result.getId());
report.setName(test.getName());
report.setDescription(test.getDescription());
report.setContent(JSONObject.toJSONString(result));
report.setCreateTime(System.currentTimeMillis());
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(APITestStatus.Completed.name());
apiTestReportMapper.insert(report);
} }
public void complete(TestResult result) {
ApiTestReport report = getRunningReport(result.getTestId());
if (report == null) {
MSException.throwException(Translator.get("api_report_is_null"));
}
report.setContent(JSONObject.toJSONString(result));
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(APITestStatus.Completed.name());
apiTestReportMapper.updateByPrimaryKeySelective(report);
}
public String create(ApiTest test) {
ApiTestReport running = getRunningReport(test.getId());
if (running != null) {
return running.getId();
}
ApiTestReport report = new ApiTestReport();
report.setId(UUID.randomUUID().toString());
report.setTestId(test.getId());
report.setName(test.getName());
report.setDescription(test.getDescription());
report.setCreateTime(System.currentTimeMillis());
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(APITestStatus.Running.name());
apiTestReportMapper.insert(report);
return report.getId();
}
public ApiTestReport getRunningReport(String testId) {
ApiTestReportExample example = new ApiTestReportExample();
example.createCriteria().andTestIdEqualTo(testId).andStatusEqualTo(APITestStatus.Running.name());
List<ApiTestReport> apiTestReports = apiTestReportMapper.selectByExample(example);
if (apiTestReports.size() > 0) {
return apiTestReports.get(0);
} else {
return null;
}
}
public List<DashboardTestDTO> dashboardTests(String workspaceId) { public List<DashboardTestDTO> dashboardTests(String workspaceId) {
Instant oneYearAgo = Instant.now().plus(-365, ChronoUnit.DAYS); Instant oneYearAgo = Instant.now().plus(-365, ChronoUnit.DAYS);
long startTimestamp = oneYearAgo.toEpochMilli(); long startTimestamp = oneYearAgo.toEpochMilli();
return extApiTestReportMapper.selectDashboardTests(workspaceId, startTimestamp); return extApiTestReportMapper.selectDashboardTests(workspaceId, startTimestamp);
} }
} }

View File

@ -39,6 +39,8 @@ public class APITestService {
private FileService fileService; private FileService fileService;
@Resource @Resource
private JMeterService jMeterService; private JMeterService jMeterService;
@Resource
private APIReportService apiReportService;
public List<APITestResult> list(QueryAPITestRequest request) { public List<APITestResult> list(QueryAPITestRequest request) {
return extApiTestMapper.list(request); return extApiTestMapper.list(request);
@ -71,18 +73,23 @@ public class APITestService {
public void delete(DeleteAPITestRequest request) { public void delete(DeleteAPITestRequest request) {
deleteFileByTestId(request.getId()); deleteFileByTestId(request.getId());
apiReportService.deleteByTestId(request.getId());
apiTestMapper.deleteByPrimaryKey(request.getId()); apiTestMapper.deleteByPrimaryKey(request.getId());
} }
public void run(SaveAPITestRequest request) { public String run(SaveAPITestRequest request) {
ApiTestFile file = getFileByTestId(request.getId()); ApiTestFile file = getFileByTestId(request.getId());
if (file == null) { if (file == null) {
MSException.throwException(Translator.get("file_cannot_be_null")); MSException.throwException(Translator.get("file_cannot_be_null"));
} }
byte[] bytes = fileService.loadFileAsBytes(file.getFileId()); byte[] bytes = fileService.loadFileAsBytes(file.getFileId());
InputStream is = new ByteArrayInputStream(bytes); InputStream is = new ByteArrayInputStream(bytes);
String reportId = apiReportService.create(get(request.getId()));
changeStatus(request.getId(), APITestStatus.Running); changeStatus(request.getId(), APITestStatus.Running);
jMeterService.run(is); jMeterService.run(is);
return reportId;
} }
public void changeStatus(String id, APITestStatus status) { public void changeStatus(String id, APITestStatus status) {

View File

@ -78,6 +78,9 @@ public class UserService {
userRole.setSourceId("adminSourceId"); userRole.setSourceId("adminSourceId");
userRoleMapper.insertSelective(userRole); userRoleMapper.insertSelective(userRole);
} else { } else {
// if (!map.keySet().contains("Ids")) {
// MSException.throwException(role + " no source id");
// }
List<String> list = (List<String>) map.get("Ids"); List<String> list = (List<String>) map.get("Ids");
for (int j = 0; j < list.size(); j++) { for (int j = 0; j < list.size(); j++) {
UserRole userRole1 = new UserRole(); UserRole userRole1 = new UserRole();
@ -316,7 +319,7 @@ public class UserService {
public void setLanguage(String lang) { public void setLanguage(String lang) {
if (SessionUtils.getUser() != null) { if (SessionUtils.getUser() != null) {
UserRequest user = new UserRequest(); User user = new User();
user.setId(SessionUtils.getUser().getId()); user.setId(SessionUtils.getUser().getId());
user.setLanguage(lang); user.setLanguage(lang);
updateUser(user); updateUser(user);

View File

@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS `file_content` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `file_metadata` ( CREATE TABLE IF NOT EXISTS `file_metadata` (
`id` varchar(64) NOT NULL COMMENT 'File ID', `id` varchar(64) NOT NULL COMMENT 'File ID',
@ -18,7 +18,7 @@ CREATE TABLE IF NOT EXISTS `file_metadata` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `load_test` ( CREATE TABLE IF NOT EXISTS `load_test` (
`id` varchar(50) NOT NULL COMMENT 'Test ID', `id` varchar(50) NOT NULL COMMENT 'Test ID',
@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS `load_test` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `load_test_file` ( CREATE TABLE IF NOT EXISTS `load_test_file` (
`test_id` varchar(64) DEFAULT NULL, `test_id` varchar(64) DEFAULT NULL,
@ -58,14 +58,14 @@ CREATE TABLE IF NOT EXISTS `load_test_report` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `load_test_report_detail` ( CREATE TABLE IF NOT EXISTS `load_test_report_detail` (
`report_id` varchar(50) NOT NULL, `report_id` varchar(50) NOT NULL,
`content` longtext, `content` longtext,
`part` bigint(11) NOT NULL, `part` bigint(11) NOT NULL,
PRIMARY KEY (`report_id`,`part`) PRIMARY KEY (`report_id`,`part`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `load_test_report_log` ( CREATE TABLE IF NOT EXISTS `load_test_report_log` (
`id` varchar(50) NOT NULL, `id` varchar(50) NOT NULL,
@ -75,7 +75,7 @@ CREATE TABLE IF NOT EXISTS `load_test_report_log` (
`part` bigint(20) DEFAULT NULL, `part` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `load_test_report_log_report_id_resource_name_index` (`report_id`,`resource_id`,`part`) KEY `load_test_report_log_report_id_resource_name_index` (`report_id`,`resource_id`,`part`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `load_test_report_result` ( CREATE TABLE IF NOT EXISTS `load_test_report_result` (
`id` varchar(50) NOT NULL, `id` varchar(50) NOT NULL,
@ -84,7 +84,7 @@ CREATE TABLE IF NOT EXISTS `load_test_report_result` (
`report_value` text , `report_value` text ,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `load_test_report_result_report_id_report_key_index` (`report_id`,`report_key`) KEY `load_test_report_result_report_id_report_key_index` (`report_id`,`report_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `organization` ( CREATE TABLE IF NOT EXISTS `organization` (
@ -97,7 +97,7 @@ CREATE TABLE IF NOT EXISTS `organization` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `project` ( CREATE TABLE IF NOT EXISTS `project` (
`id` varchar(50) NOT NULL COMMENT 'Project ID', `id` varchar(50) NOT NULL COMMENT 'Project ID',
@ -110,7 +110,7 @@ CREATE TABLE IF NOT EXISTS `project` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `role` ( CREATE TABLE IF NOT EXISTS `role` (
`id` varchar(50) NOT NULL COMMENT 'Role ID', `id` varchar(50) NOT NULL COMMENT 'Role ID',
@ -123,7 +123,7 @@ CREATE TABLE IF NOT EXISTS `role` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `system_parameter` ( CREATE TABLE IF NOT EXISTS `system_parameter` (
`param_key` varchar(64) CHARACTER SET utf8mb4 NOT NULL COMMENT 'Parameter name', `param_key` varchar(64) CHARACTER SET utf8mb4 NOT NULL COMMENT 'Parameter name',
@ -134,7 +134,7 @@ CREATE TABLE IF NOT EXISTS `system_parameter` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `test_resource` ( CREATE TABLE IF NOT EXISTS `test_resource` (
`id` varchar(50) NOT NULL COMMENT 'Test resource ID', `id` varchar(50) NOT NULL COMMENT 'Test resource ID',
@ -147,7 +147,7 @@ CREATE TABLE IF NOT EXISTS `test_resource` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `test_resource_pool` ( CREATE TABLE IF NOT EXISTS `test_resource_pool` (
`id` varchar(50) NOT NULL COMMENT 'Test resource pool ID', `id` varchar(50) NOT NULL COMMENT 'Test resource pool ID',
@ -161,13 +161,13 @@ CREATE TABLE IF NOT EXISTS `test_resource_pool` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user` ( CREATE TABLE IF NOT EXISTS `user` (
`id` varchar(50) NOT NULL COMMENT 'User ID', `id` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT 'User ID',
`name` varchar(64) NOT NULL COMMENT 'User name', `name` varchar(64) NOT NULL COMMENT 'User name',
`email` varchar(64) NOT NULL COMMENT 'E-Mail address', `email` varchar(64) NOT NULL COMMENT 'E-Mail address',
`password` varchar(256) DEFAULT NULL, `password` varchar(256) COLLATE utf8mb4_bin DEFAULT NULL,
`status` varchar(50) NOT NULL COMMENT 'User status', `status` varchar(50) NOT NULL COMMENT 'User status',
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp', `create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
`update_time` bigint(13) NOT NULL COMMENT 'Update timestamp', `update_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
@ -179,7 +179,7 @@ CREATE TABLE IF NOT EXISTS `user` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `user_role` ( CREATE TABLE IF NOT EXISTS `user_role` (
`id` varchar(50) NOT NULL COMMENT 'ID of user''s role info', `id` varchar(50) NOT NULL COMMENT 'ID of user''s role info',
@ -192,7 +192,7 @@ CREATE TABLE IF NOT EXISTS `user_role` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `workspace` ( CREATE TABLE IF NOT EXISTS `workspace` (
`id` varchar(50) NOT NULL COMMENT 'Workspace ID ', `id` varchar(50) NOT NULL COMMENT 'Workspace ID ',
@ -205,7 +205,7 @@ CREATE TABLE IF NOT EXISTS `workspace` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
-- api start -- api start
@ -220,7 +220,7 @@ CREATE TABLE IF NOT EXISTS `api_test` (
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp', `create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
`update_time` bigint(13) NOT NULL COMMENT 'Update timestamp', `update_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `api_test_file` ( CREATE TABLE IF NOT EXISTS `api_test_file` (
`test_id` varchar(64) DEFAULT NULL, `test_id` varchar(64) DEFAULT NULL,
@ -243,7 +243,7 @@ CREATE TABLE IF NOT EXISTS `api_test_report` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
-- api end -- api end
@ -268,7 +268,7 @@ CREATE TABLE IF NOT EXISTS `test_plan` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `test_case_node` ( CREATE TABLE IF NOT EXISTS `test_case_node` (
@ -283,7 +283,7 @@ CREATE TABLE IF NOT EXISTS `test_case_node` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `test_case` ( CREATE TABLE IF NOT EXISTS `test_case` (
@ -305,7 +305,7 @@ CREATE TABLE IF NOT EXISTS `test_case` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `test_plan_test_case` ( CREATE TABLE IF NOT EXISTS `test_plan_test_case` (
@ -323,7 +323,7 @@ CREATE TABLE IF NOT EXISTS `test_plan_test_case` (
) )
ENGINE = InnoDB ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin; COLLATE = utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `test_case_report_template` ( CREATE TABLE IF NOT EXISTS `test_case_report_template` (
`id` varchar(50) NOT NULL, `id` varchar(50) NOT NULL,
@ -334,7 +334,7 @@ CREATE TABLE IF NOT EXISTS `test_case_report_template` (
) )
ENGINE=InnoDB ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4 DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin; COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `test_case_report` ( CREATE TABLE IF NOT EXISTS `test_case_report` (
`id` varchar(50) NOT NULL, `id` varchar(50) NOT NULL,
@ -346,7 +346,7 @@ CREATE TABLE IF NOT EXISTS `test_case_report` (
) )
ENGINE=InnoDB ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4 DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin; COLLATE=utf8mb4_general_ci;
-- track end -- track end

View File

@ -37,6 +37,7 @@ organization_does_not_belong_to_user=The current organization does not belong to
organization_id_is_null=Organization ID cannot be null organization_id_is_null=Organization ID cannot be null
#api #api
api_load_script_error=Load script error api_load_script_error=Load script error
api_report_is_null="Report is null, can't update"
#test case #test case
test_case_node_level=level test_case_node_level=level
test_case_node_level_tip=The node tree maximum depth is test_case_node_level_tip=The node tree maximum depth is

View File

@ -37,6 +37,7 @@ organization_does_not_belong_to_user=当前组织不属于当前用户
organization_id_is_null=组织 ID 不能为空 organization_id_is_null=组织 ID 不能为空
#api #api
api_load_script_error=读取脚本失败 api_load_script_error=读取脚本失败
api_report_is_null="测试报告是未生成,无法更新"
#test case #test case
test_case_node_level= test_case_node_level=
test_case_node_level_tip=模块树最大深度为 test_case_node_level_tip=模块树最大深度为

View File

@ -37,6 +37,7 @@ organization_does_not_belong_to_user=當前組織不屬於當前用戶
organization_id_is_null=組織 ID 不能為空 organization_id_is_null=組織 ID 不能為空
#api #api
api_load_script_error=讀取腳本失敗 api_load_script_error=讀取腳本失敗
api_report_is_null="測試報告是未生成,無法更新"
#test case #test case
test_case_node_level= test_case_node_level=
test_case_node_level_tip=模塊樹最大深度為 test_case_node_level_tip=模塊樹最大深度為

View File

@ -5,9 +5,9 @@
<section class="report-container" v-if="this.report.testId"> <section class="report-container" v-if="this.report.testId">
<header class="report-header"> <header class="report-header">
<span>{{report.projectName}} / </span> <span>{{report.projectName}} / </span>
<router-link :to="path">{{report.testName}}</router-link> <router-link :to="path">{{report.testName}} [{{report.createTime | timestampFormatDate}}]</router-link>
</header> </header>
<main> <main v-if="this.isCompleted">
<div class="scenario-chart"> <div class="scenario-chart">
<ms-metric-chart :content="content"></ms-metric-chart> <ms-metric-chart :content="content"></ms-metric-chart>
</div> </div>
@ -59,11 +59,17 @@
methods: { methods: {
getReport() { getReport() {
this.report = {};
this.content = {};
if (this.reportId) { if (this.reportId) {
let url = "/api/report/get/" + this.reportId; let url = "/api/report/get/" + this.reportId;
this.result = this.$get(url, response => { this.result = this.$get(url, response => {
this.report = response.data || {}; this.report = response.data || {};
this.content = JSON.parse(this.report.content); if (this.isCompleted) {
this.content = JSON.parse(this.report.content);
} else {
setTimeout(this.getReport, 2000)
}
}); });
} }
} }
@ -83,6 +89,9 @@
}, },
path() { path() {
return "/api/test/edit?id=" + this.report.testId; return "/api/test/edit?id=" + this.report.testId;
},
isCompleted() {
return "Completed" === this.report.status;
} }
} }
} }

View File

@ -96,8 +96,8 @@
show: false show: false
}, },
data: [ data: [
{value: this.content.success, name: this.$t('api_report.success')}, {value: this.content.success},
{value: this.content.error, name: this.$t('api_report.fail')}, {value: this.content.error},
] ]
} }
] ]

View File

@ -120,8 +120,11 @@
}) })
}, },
runTest: function () { runTest: function () {
this.result = this.$post("/api/run", {id: this.test.id}, () => { this.result = this.$post("/api/run", {id: this.test.id}, (response) => {
this.$success(this.$t('api_test.running')); this.$success(this.$t('api_test.running'));
this.$router.push({
path: '/api/report/view/' + response.data
})
}); });
}, },
saveRunTest: function () { saveRunTest: function () {

View File

@ -1,38 +1,44 @@
<template> <template>
<div class="request-container"> <div class="request-container">
<div class="request-item" v-for="(request, index) in requests" :key="index" @click="select(request)" <draggable :list="requests" group="Request" class="request-draggable" ghost-class="request-ghost">
:class="{'selected': isSelected(request)}"> <div class="request-item" v-for="(request, index) in requests" :key="index" @click="select(request)"
<el-row type="flex"> :class="{'selected': isSelected(request)}">
<div class="request-method"> <el-row type="flex">
{{request.method}} <div class="request-method">
</div> {{request.method}}
<div class="request-name"> </div>
{{request.name}} <div class="request-name">
</div> {{request.name}}
<div class="request-btn"> </div>
<el-dropdown trigger="click" @command="handleCommand"> <div class="request-btn">
<span class="el-dropdown-link el-icon-more"/> <el-dropdown trigger="click" @command="handleCommand">
<el-dropdown-menu slot="dropdown"> <span class="el-dropdown-link el-icon-more"/>
<el-dropdown-item :command="{type: 'copy', index: index}"> <el-dropdown-menu slot="dropdown">
{{$t('api_test.request.copy')}} <el-dropdown-item :command="{type: 'copy', index: index}">
</el-dropdown-item> {{$t('api_test.request.copy')}}
<el-dropdown-item :command="{type: 'delete', index: index}"> </el-dropdown-item>
{{$t('api_test.request.delete')}} <el-dropdown-item :command="{type: 'delete', index: index}">
</el-dropdown-item> {{$t('api_test.request.delete')}}
</el-dropdown-menu> </el-dropdown-item>
</el-dropdown> </el-dropdown-menu>
</div> </el-dropdown>
</el-row> </div>
</div> </el-row>
</div>
</draggable>
<el-button class="request-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createRequest"/> <el-button class="request-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createRequest"/>
</div> </div>
</template> </template>
<script> <script>
import {Request} from "../model/ScenarioModel"; import {Request} from "../model/ScenarioModel";
import draggable from 'vuedraggable';
export default { export default {
name: "MsApiRequestConfig", name: "MsApiRequestConfig",
components: {draggable},
props: { props: {
requests: Array, requests: Array,
open: Function open: Function
@ -136,4 +142,9 @@
.request-create { .request-create {
width: 100%; width: 100%;
} }
.request-ghost {
opacity: 0.5;
background-color: #909399;
}
</style> </style>

View File

@ -3,29 +3,31 @@
<el-aside class="scenario-aside"> <el-aside class="scenario-aside">
<div class="scenario-list"> <div class="scenario-list">
<ms-api-collapse v-model="activeName" @change="handleChange" accordion> <ms-api-collapse v-model="activeName" @change="handleChange" accordion>
<ms-api-collapse-item v-for="(scenario, index) in scenarios" :key="index" <draggable :list="scenarios" group="Scenario" class="scenario-draggable" ghost-class="scenario-ghost">
:title="scenario.name" :name="index"> <ms-api-collapse-item v-for="(scenario, index) in scenarios" :key="index"
<template slot="title"> :title="scenario.name" :name="index">
<div class="scenario-name"> <template slot="title">
{{scenario.name}} <div class="scenario-name">
<span id="hint" v-if="!scenario.name"> {{scenario.name}}
<span id="hint" v-if="!scenario.name">
{{$t('api_test.scenario.config')}} {{$t('api_test.scenario.config')}}
</span> </span>
</div> </div>
<el-dropdown trigger="click" @command="handleCommand"> <el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link el-icon-more scenario-btn"/> <span class="el-dropdown-link el-icon-more scenario-btn"/>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{type: 'copy', index: index}"> <el-dropdown-item :command="{type: 'copy', index: index}">
{{$t('api_test.scenario.copy')}} {{$t('api_test.scenario.copy')}}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item :command="{type:'delete', index:index}"> <el-dropdown-item :command="{type:'delete', index:index}">
{{$t('api_test.scenario.delete')}} {{$t('api_test.scenario.delete')}}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</template> </template>
<ms-api-request-config :requests="scenario.requests" :open="select"/> <ms-api-request-config :requests="scenario.requests" :open="select"/>
</ms-api-collapse-item> </ms-api-collapse-item>
</draggable>
</ms-api-collapse> </ms-api-collapse>
</div> </div>
<el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createScenario"/> <el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createScenario"/>
@ -48,6 +50,7 @@
import MsApiRequestForm from "./ApiRequestForm"; import MsApiRequestForm from "./ApiRequestForm";
import MsApiScenarioForm from "./ApiScenarioForm"; import MsApiScenarioForm from "./ApiScenarioForm";
import {Scenario, Request} from "../model/ScenarioModel"; import {Scenario, Request} from "../model/ScenarioModel";
import draggable from 'vuedraggable';
export default { export default {
name: "MsApiScenarioConfig", name: "MsApiScenarioConfig",
@ -57,7 +60,8 @@
MsApiScenarioForm, MsApiScenarioForm,
MsApiRequestForm, MsApiRequestForm,
MsApiCollapse, MsApiCollapse,
MsApiCollapseItem MsApiCollapseItem,
draggable
}, },
props: { props: {
@ -183,4 +187,12 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
} }
.scenario-ghost {
opacity: 0.5;
}
.scenario-draggable {
background-color: #909399;
}
</style> </style>

View File

@ -39,7 +39,8 @@
default: false default: false
}, },
index: Number, index: Number,
list: Array list: Array,
callback: Function
}, },
data() { data() {
@ -51,6 +52,7 @@
methods: { methods: {
add: function () { add: function () {
this.list.push(new Regex(this.regex)); this.list.push(new Regex(this.regex));
this.callback();
}, },
remove: function () { remove: function () {
this.list.splice(this.index, 1); this.list.splice(this.index, 1);

View File

@ -21,7 +21,8 @@
props: { props: {
edit: Boolean, edit: Boolean,
duration: ResponseTime duration: ResponseTime,
callback: Function
}, },
data() { data() {
@ -34,6 +35,7 @@
add: function () { add: function () {
setTimeout(() => { setTimeout(() => {
this.duration.value = this.time; this.duration.value = this.time;
this.callback();
}) })
}, },
remove: function () { remove: function () {

View File

@ -37,7 +37,8 @@
name: "MsApiAssertionText", name: "MsApiAssertionText",
props: { props: {
list: Array list: Array,
callback: Function
}, },
data() { data() {
@ -52,6 +53,7 @@
methods: { methods: {
add: function () { add: function () {
this.list.push(this.toRegex()); this.list.push(this.toRegex());
this.callback();
}, },
toRegex: function () { toRegex: function () {
let expression = ""; let expression = "";

View File

@ -10,9 +10,10 @@
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="20"> <el-col :span="20">
<ms-api-assertion-text :list="assertions.regex" v-if="type === options.TEXT"/> <ms-api-assertion-text :list="assertions.regex" v-if="type === options.TEXT" :callback="after"/>
<ms-api-assertion-regex :list="assertions.regex" v-if="type === options.REGEX"/> <ms-api-assertion-regex :list="assertions.regex" v-if="type === options.REGEX" :callback="after"/>
<ms-api-assertion-response-time :duration="assertions.duration" v-if="type === options.RESPONSE_TIME"/> <ms-api-assertion-response-time :duration="assertions.duration" v-if="type === options.RESPONSE_TIME"
:callback="after"/>
</el-col> </el-col>
</el-row> </el-row>
@ -41,7 +42,14 @@
options: ASSERTION_TYPE, options: ASSERTION_TYPE,
type: "", type: "",
} }
},
methods: {
after() {
this.type = "";
}
} }
} }
</script> </script>

View File

@ -13,7 +13,7 @@
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="20"> <el-col :span="20">
<ms-api-extract-common :extract-type="type" :list="list" v-if="type"/> <ms-api-extract-common :extract-type="type" :list="list" v-if="type" :callback="after"/>
</el-col> </el-col>
</el-row> </el-row>
@ -45,6 +45,12 @@
} }
}, },
methods: {
after() {
this.type = "";
}
},
computed: { computed: {
list() { list() {
switch (this.type) { switch (this.type) {

View File

@ -41,7 +41,8 @@
default: false default: false
}, },
index: Number, index: Number,
list: Array list: Array,
callback: Function
}, },
data() { data() {
@ -54,6 +55,7 @@
add() { add() {
this.list.push(new ExtractCommon(this.extractType, this.common)); this.list.push(new ExtractCommon(this.extractType, this.common));
this.clear(); this.clear();
this.callback();
}, },
change(variable) { change(variable) {
this.common.value = "${" + variable + "}"; this.common.value = "${" + variable + "}";

View File

@ -68,20 +68,28 @@
<el-input v-model="form.password" autocomplete="off" show-password/> <el-input v-model="form.password" autocomplete="off" show-password/>
</el-form-item> </el-form-item>
<div v-for="(role, index) in form.roles" :key="index"> <div v-for="(role, index) in form.roles" :key="index">
<el-form-item :label="'角色'+index" :required="true"> <el-form-item :label="'角色'+index"
:prop="'roles.' + index + '.id'"
:rules="{required: true, message: '请选择角色', trigger: 'change'}"
>
<el-select v-model="role.id" placeholder="选择角色类型"> <el-select v-model="role.id" placeholder="选择角色类型">
<el-option <el-option
v-for="item in userRole" v-for="item in activeRole(role)"
:key="item.id" :key="item.id"
:label="item.name" :label="item.name"
:value="item.id"> :value="item.id"
>
{{item.name}}
</el-option> </el-option>
</el-select> </el-select>
<el-button @click.prevent="removeRole(role)" style="margin-left: 20px;" v-if="form.roles.length > 1">删除 <el-button @click.prevent="removeRole(role)" style="margin-left: 20px;" v-if="form.roles.length > 1">删除
</el-button> </el-button>
</el-form-item> </el-form-item>
<div v-if="role.id === 'org_admin'"> <div v-if="role.id === 'org_admin'">
<el-form-item label="选择组织" :required="true"> <el-form-item label="选择组织"
:prop="'roles.' + index + '.Ids'"
:rules="{required: true, message: '请选择组织', trigger: 'change'}"
>
<el-select v-model="role.Ids" placeholder="选择组织" multiple> <el-select v-model="role.Ids" placeholder="选择组织" multiple>
<el-option <el-option
v-for="item in form.orgList" v-for="item in form.orgList"
@ -93,7 +101,10 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id === 'test_manager'"> <div v-if="role.id === 'test_manager'">
<el-form-item label="选择工作空间" :required="true"> <el-form-item label="选择工作空间"
:prop="'roles.' + index + '.Ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
>
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple> <el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
@ -105,7 +116,10 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id ==='test_user'"> <div v-if="role.id ==='test_user'">
<el-form-item label="选择工作空间" :required="true"> <el-form-item label="选择工作空间"
:prop="'roles.' + index + '.Ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
>
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple> <el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
@ -117,7 +131,10 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id ==='test_viewer'"> <div v-if="role.id ==='test_viewer'">
<el-form-item label="选择工作空间" :required="true"> <el-form-item label="选择工作空间"
:prop="'roles.' + index + '.Ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
>
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple> <el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
@ -132,7 +149,7 @@
<el-form-item> <el-form-item>
<template> <template>
<el-button type="success" style="width: 100%;" @click="addRole()">添加角色</el-button> <el-button type="success" style="width: 100%;" @click="addRole('createUserForm')" :disabled="btnAddRole">添加角色</el-button>
</template> </template>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -160,10 +177,13 @@
<el-input v-model="form.phone" autocomplete="off"/> <el-input v-model="form.phone" autocomplete="off"/>
</el-form-item> </el-form-item>
<div v-for="(role, index) in form.roles" :key="index"> <div v-for="(role, index) in form.roles" :key="index">
<el-form-item :label="'角色'+index" :required="true"> <el-form-item :label="'角色'+index"
:prop="'roles.' + index + '.id'"
:rules="{required: true, message: '请选择角色', trigger: 'change'}"
>
<el-select v-model="role.id" placeholder="选择角色类型"> <el-select v-model="role.id" placeholder="选择角色类型">
<el-option <el-option
v-for="item in userRole" v-for="item in activeRole(role)"
:key="item.id" :key="item.id"
:label="item.name" :label="item.name"
:value="item.id"> :value="item.id">
@ -173,7 +193,10 @@
</el-button> </el-button>
</el-form-item> </el-form-item>
<div v-if="role.id === 'org_admin'"> <div v-if="role.id === 'org_admin'">
<el-form-item label="选择组织" :required="true"> <el-form-item label="选择组织"
:prop="'roles.' + index + '.Ids'"
:rules="{required: true, message: '请选择组织', trigger: 'change'}"
>
<el-select v-model="role.Ids" placeholder="选择组织" multiple> <el-select v-model="role.Ids" placeholder="选择组织" multiple>
<el-option <el-option
v-for="item in form.orgList" v-for="item in form.orgList"
@ -185,7 +208,10 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id === 'test_manager'"> <div v-if="role.id === 'test_manager'">
<el-form-item label="选择工作空间" :required="true"> <el-form-item label="选择工作空间"
:prop="'roles.' + index + '.Ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
>
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple> <el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
@ -197,7 +223,10 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id ==='test_user'"> <div v-if="role.id ==='test_user'">
<el-form-item label="选择工作空间" :required="true"> <el-form-item label="选择工作空间"
:prop="'roles.' + index + '.Ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
>
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple> <el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
@ -209,7 +238,10 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id ==='test_viewer'"> <div v-if="role.id ==='test_viewer'">
<el-form-item label="选择工作空间" :required="true"> <el-form-item label="选择工作空间"
:prop="'roles.' + index + '.Ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
>
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple> <el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
@ -223,7 +255,7 @@
</div> </div>
<el-form-item> <el-form-item>
<template> <template>
<el-button type="success" style="width: 100%;" @click="addRole()">添加角色</el-button> <el-button type="success" style="width: 100%;" @click="addRole('updateUserForm')" :disabled="btnAddRole">添加角色</el-button>
</template> </template>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -286,6 +318,7 @@
createVisible: false, createVisible: false,
updateVisible: false, updateVisible: false,
editPasswordVisible: false, editPasswordVisible: false,
btnAddRole: false,
multipleSelection: [], multipleSelection: [],
userRole: [], userRole: [],
currentPage: 1, currentPage: 1,
@ -294,11 +327,12 @@
condition: {}, condition: {},
tableData: [], tableData: [],
form: { form: {
roles: [{}] roles: [{
id: ''
}]
}, },
checkPasswordForm: {}, checkPasswordForm: {},
ruleForm: {}, ruleForm: {},
setAdminParam: {},
rule: { rule: {
id: [ id: [
{required: true, message: this.$t('user.input_id'), trigger: 'blur'}, {required: true, message: this.$t('user.input_id'), trigger: 'blur'},
@ -451,7 +485,8 @@
}) })
}, },
handleClose() { handleClose() {
this.form = {roles: [{value: ''}]}; this.form = {roles: [{id: ''}]};
this.btnAddRole = false;
}, },
changeSwitch(row) { changeSwitch(row) {
this.$post(this.updatePath, row, () => { this.$post(this.updatePath, row, () => {
@ -479,16 +514,51 @@
this.userRole = response.data; this.userRole = response.data;
}) })
}, },
addRole() { addRole(validForm) {
let roles = this.form.roles; this.$refs[validForm].validate(valid => {
roles.push({}); if (valid) {
let roleInfo = {};
roleInfo.selects = [];
let ids = this.form.roles.map(r => r.id);
ids.forEach(id => {
roleInfo.selects.push(id);
})
let roles = this.form.roles;
roles.push(roleInfo);
if (this.form.roles.length > this.userRole.length - 1) {
this.btnAddRole = true;
}
} else {
return false;
}
})
}, },
removeRole(item) { removeRole(item) {
let index = this.form.roles.indexOf(item) let index = this.form.roles.indexOf(item);
if (index !== -1) { if (index !== -1) {
this.form.roles.splice(index, 1) this.form.roles.splice(index, 1)
} }
if (this.form.roles.length < this.userRole.length) {
this.btnAddRole = false;
}
}, },
activeRole(roleInfo) {
return this.userRole.filter(function (role) {
let value = true;
if (!roleInfo.selects) {
return true;
}
if (roleInfo.selects.length === 0) {
value = true;
}
for (let i = 0; i < roleInfo.selects.length; i++) {
if (role.id === roleInfo.selects[i]) {
value = false;
}
}
return value;
})
}
} }
} }
</script> </script>