fix: 解决冲突
This commit is contained in:
commit
992b367f53
|
@ -7,6 +7,7 @@ import io.metersphere.api.dto.ApiTestImportRequest;
|
||||||
import io.metersphere.api.dto.automation.ApiScenarioRequest;
|
import io.metersphere.api.dto.automation.ApiScenarioRequest;
|
||||||
import io.metersphere.api.dto.automation.ReferenceDTO;
|
import io.metersphere.api.dto.automation.ReferenceDTO;
|
||||||
import io.metersphere.api.dto.definition.*;
|
import io.metersphere.api.dto.definition.*;
|
||||||
|
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
|
||||||
import io.metersphere.api.service.ApiDefinitionService;
|
import io.metersphere.api.service.ApiDefinitionService;
|
||||||
import io.metersphere.base.domain.ApiDefinition;
|
import io.metersphere.base.domain.ApiDefinition;
|
||||||
import io.metersphere.commons.constants.RoleConstants;
|
import io.metersphere.commons.constants.RoleConstants;
|
||||||
|
@ -93,7 +94,7 @@ public class ApiDefinitionController {
|
||||||
|
|
||||||
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
|
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
|
||||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||||
public String testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
|
public ApiDefinitionImport testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
|
||||||
return apiDefinitionService.apiTestImport(file, request);
|
return apiDefinitionService.apiTestImport(file, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,5 +13,7 @@ public class ApiTestImportRequest {
|
||||||
private String projectId;
|
private String projectId;
|
||||||
private String platform;
|
private String platform;
|
||||||
private Boolean useEnvironment;
|
private Boolean useEnvironment;
|
||||||
|
// 来自场景的导入不需要存储
|
||||||
|
private boolean saved = true;
|
||||||
private String swaggerUrl;
|
private String swaggerUrl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class MsParser extends ApiImportAbstractParser {
|
||||||
if (testObject.get("projectName") != null) {
|
if (testObject.get("projectName") != null) {
|
||||||
return parseMsFormat(testStr, request);
|
return parseMsFormat(testStr, request);
|
||||||
} else {
|
} else {
|
||||||
return parsePluginFormat(testObject);
|
return parsePluginFormat(testObject, request.isSaved());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,14 +53,16 @@ public class MsParser extends ApiImportAbstractParser {
|
||||||
return apiDefinitionImport;
|
return apiDefinitionImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ApiDefinitionImport parsePluginFormat(JSONObject testObject) {
|
private ApiDefinitionImport parsePluginFormat(JSONObject testObject, boolean isSaved) {
|
||||||
List<ApiDefinitionResult> results = new ArrayList<>();
|
List<ApiDefinitionResult> results = new ArrayList<>();
|
||||||
ApiDefinitionImport apiImport = new ApiDefinitionImport();
|
ApiDefinitionImport apiImport = new ApiDefinitionImport();
|
||||||
apiImport.setProtocol(RequestType.HTTP);
|
apiImport.setProtocol(RequestType.HTTP);
|
||||||
apiImport.setData(results);
|
apiImport.setData(results);
|
||||||
testObject.keySet().forEach(tag -> {
|
testObject.keySet().forEach(tag -> {
|
||||||
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
|
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
|
||||||
|
if (isSaved) {
|
||||||
createModule(module);
|
createModule(module);
|
||||||
|
}
|
||||||
JSONObject requests = testObject.getJSONObject(tag);
|
JSONObject requests = testObject.getJSONObject(tag);
|
||||||
requests.keySet().forEach(requestName -> {
|
requests.keySet().forEach(requestName -> {
|
||||||
|
|
||||||
|
|
|
@ -30,17 +30,17 @@ public class PostmanParser extends ApiImportAbstractParser {
|
||||||
List<PostmanKeyValue> variables = postmanCollection.getVariable();
|
List<PostmanKeyValue> variables = postmanCollection.getVariable();
|
||||||
ApiDefinitionImport apiImport = new ApiDefinitionImport();
|
ApiDefinitionImport apiImport = new ApiDefinitionImport();
|
||||||
List<ApiDefinitionResult> results = new ArrayList<>();
|
List<ApiDefinitionResult> results = new ArrayList<>();
|
||||||
parseItem(postmanCollection.getItem(), variables, results, buildModule(postmanCollection.getInfo().getName(), null));
|
parseItem(postmanCollection.getItem(), variables, results, buildModule(postmanCollection.getInfo().getName(), null, request.isSaved()), request.isSaved());
|
||||||
apiImport.setData(results);
|
apiImport.setData(results);
|
||||||
return apiImport;
|
return apiImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseItem(List<PostmanItem> items, List<PostmanKeyValue> variables, List<ApiDefinitionResult> results, ApiModule parentModule) {
|
private void parseItem(List<PostmanItem> items, List<PostmanKeyValue> variables, List<ApiDefinitionResult> results, ApiModule parentModule, boolean isSaved) {
|
||||||
for (PostmanItem item : items) {
|
for (PostmanItem item : items) {
|
||||||
List<PostmanItem> childItems = item.getItem();
|
List<PostmanItem> childItems = item.getItem();
|
||||||
if (childItems != null) {
|
if (childItems != null) {
|
||||||
ApiModule module = buildModule(item.getName(), parentModule);
|
ApiModule module = buildModule(item.getName(), parentModule, isSaved);
|
||||||
parseItem(childItems, variables, results, module);
|
parseItem(childItems, variables, results, module, isSaved);
|
||||||
} else {
|
} else {
|
||||||
ApiDefinitionResult request = parsePostman(item);
|
ApiDefinitionResult request = parsePostman(item);
|
||||||
if (request != null) {
|
if (request != null) {
|
||||||
|
@ -53,7 +53,7 @@ public class PostmanParser extends ApiImportAbstractParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ApiModule buildModule(String name, ApiModule parentModule) {
|
private ApiModule buildModule(String name, ApiModule parentModule, boolean isSaved) {
|
||||||
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
|
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
|
||||||
ApiModule module;
|
ApiModule module;
|
||||||
if (parentModule != null) {
|
if (parentModule != null) {
|
||||||
|
@ -62,7 +62,9 @@ public class PostmanParser extends ApiImportAbstractParser {
|
||||||
} else {
|
} else {
|
||||||
module = apiModuleService.getNewModule(name, this.projectId, 1);
|
module = apiModuleService.getNewModule(name, this.projectId, 1);
|
||||||
}
|
}
|
||||||
|
if (isSaved) {
|
||||||
createModule(module);
|
createModule(module);
|
||||||
|
}
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,11 +39,11 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
||||||
}
|
}
|
||||||
ApiDefinitionImport definitionImport = new ApiDefinitionImport();
|
ApiDefinitionImport definitionImport = new ApiDefinitionImport();
|
||||||
this.projectId = request.getProjectId();
|
this.projectId = request.getProjectId();
|
||||||
definitionImport.setData(parseRequests(swagger));
|
definitionImport.setData(parseRequests(swagger, request.isSaved()));
|
||||||
return definitionImport;
|
return definitionImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ApiDefinitionResult> parseRequests(Swagger swagger) {
|
private List<ApiDefinitionResult> parseRequests(Swagger swagger, boolean isSaved) {
|
||||||
Map<String, Path> paths = swagger.getPaths();
|
Map<String, Path> paths = swagger.getPaths();
|
||||||
Set<String> pathNames = paths.keySet();
|
Set<String> pathNames = paths.keySet();
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
||||||
parseParameters(operation, request);
|
parseParameters(operation, request);
|
||||||
apiDefinition.setRequest(JSON.toJSONString(request));
|
apiDefinition.setRequest(JSON.toJSONString(request));
|
||||||
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses())));
|
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses())));
|
||||||
buildModule(apiDefinition, operation);
|
buildModule(apiDefinition, operation, isSaved);
|
||||||
results.add(apiDefinition);
|
results.add(apiDefinition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,13 +71,15 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildModule(ApiDefinitionResult apiDefinition, Operation operation) {
|
private void buildModule(ApiDefinitionResult apiDefinition, Operation operation, boolean isSaved) {
|
||||||
List<String> tags = operation.getTags();
|
List<String> tags = operation.getTags();
|
||||||
if (tags != null) {
|
if (tags != null) {
|
||||||
tags.forEach(tag -> {
|
tags.forEach(tag -> {
|
||||||
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
|
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
|
||||||
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
|
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
|
||||||
|
if (isSaved) {
|
||||||
createModule(module);
|
createModule(module);
|
||||||
|
}
|
||||||
apiDefinition.setModuleId(module.getId());
|
apiDefinition.setModuleId(module.getId());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -304,7 +306,7 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
||||||
private void parseFormDataParameters(FormParameter parameter, Body body) {
|
private void parseFormDataParameters(FormParameter parameter, Body body) {
|
||||||
List<KeyValue> keyValues = Optional.ofNullable(body.getKvs()).orElse(new ArrayList<>());
|
List<KeyValue> keyValues = Optional.ofNullable(body.getKvs()).orElse(new ArrayList<>());
|
||||||
KeyValue kv = new KeyValue(parameter.getName(), "", getDefaultStringValue(parameter.getDescription()));
|
KeyValue kv = new KeyValue(parameter.getName(), "", getDefaultStringValue(parameter.getDescription()));
|
||||||
if (StringUtils.equals(parameter.getType(), "file") ) {
|
if (StringUtils.equals(parameter.getType(), "file")) {
|
||||||
kv.setType("file");
|
kv.setType("file");
|
||||||
}
|
}
|
||||||
keyValues.add(kv);
|
keyValues.add(kv);
|
||||||
|
|
|
@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON;
|
||||||
import io.metersphere.api.jmeter.TestResult;
|
import io.metersphere.api.jmeter.TestResult;
|
||||||
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
||||||
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
|
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
|
|
||||||
import io.metersphere.commons.utils.SessionUtils;
|
import io.metersphere.commons.utils.SessionUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -18,14 +17,9 @@ import java.util.UUID;
|
||||||
public class ApiDefinitionExecResultService {
|
public class ApiDefinitionExecResultService {
|
||||||
@Resource
|
@Resource
|
||||||
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
|
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
|
||||||
@Resource
|
|
||||||
private ExtApiDefinitionExecResultMapper extApiDefinitionExecResultMapper;
|
|
||||||
|
|
||||||
|
|
||||||
public void saveApiResult(TestResult result) {
|
public void saveApiResult(TestResult result) {
|
||||||
result.getScenarios().get(0).getRequestResults().forEach(item -> {
|
result.getScenarios().get(0).getRequestResults().forEach(item -> {
|
||||||
// 清理原始资源,每个执行 保留一条结果
|
|
||||||
extApiDefinitionExecResultMapper.deleteByResourceId(item.getName());
|
|
||||||
ApiDefinitionExecResult saveResult = new ApiDefinitionExecResult();
|
ApiDefinitionExecResult saveResult = new ApiDefinitionExecResult();
|
||||||
saveResult.setId(UUID.randomUUID().toString());
|
saveResult.setId(UUID.randomUUID().toString());
|
||||||
saveResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
|
saveResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
|
||||||
|
|
|
@ -326,7 +326,7 @@ public class ApiDefinitionService {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public APIReportResult getDbResult(String testId) {
|
public APIReportResult getDbResult(String testId) {
|
||||||
ApiDefinitionExecResult result = extApiDefinitionExecResultMapper.selectByResourceId(testId);
|
ApiDefinitionExecResult result = extApiDefinitionExecResultMapper.selectMaxResultByResourceId(testId);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -336,7 +336,7 @@ public class ApiDefinitionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String apiTestImport(MultipartFile file, ApiTestImportRequest request) {
|
public ApiDefinitionImport apiTestImport(MultipartFile file, ApiTestImportRequest request) {
|
||||||
ApiImportParser apiImportParser = ApiImportParserFactory.getApiImportParser(request.getPlatform());
|
ApiImportParser apiImportParser = ApiImportParserFactory.getApiImportParser(request.getPlatform());
|
||||||
ApiDefinitionImport apiImport = null;
|
ApiDefinitionImport apiImport = null;
|
||||||
try {
|
try {
|
||||||
|
@ -345,8 +345,10 @@ public class ApiDefinitionService {
|
||||||
LogUtil.error(e.getMessage(), e);
|
LogUtil.error(e.getMessage(), e);
|
||||||
MSException.throwException(Translator.get("parse_data_error"));
|
MSException.throwException(Translator.get("parse_data_error"));
|
||||||
}
|
}
|
||||||
|
if (request.isSaved()) {
|
||||||
importApiTest(request, apiImport);
|
importApiTest(request, apiImport);
|
||||||
return "SUCCESS";
|
}
|
||||||
|
return apiImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importApiTest(ApiTestImportRequest importRequest, ApiDefinitionImport apiImport) {
|
private void importApiTest(ApiTestImportRequest importRequest, ApiDefinitionImport apiImport) {
|
||||||
|
|
|
@ -6,7 +6,6 @@ public interface ExtApiDefinitionExecResultMapper {
|
||||||
|
|
||||||
void deleteByResourceId(String id);
|
void deleteByResourceId(String id);
|
||||||
|
|
||||||
ApiDefinitionExecResult selectByResourceId(String resourceId);
|
ApiDefinitionExecResult selectMaxResultByResourceId(String resourceId);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -5,8 +5,8 @@
|
||||||
delete from api_definition_exec_result where resource_id = #{id,jdbcType=VARCHAR}
|
delete from api_definition_exec_result where resource_id = #{id,jdbcType=VARCHAR}
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
<select id="selectByResourceId" parameterType="java.lang.String" resultType="io.metersphere.base.domain.ApiDefinitionExecResult">
|
<select id="selectMaxResultByResourceId" parameterType="java.lang.String" resultType="io.metersphere.base.domain.ApiDefinitionExecResult">
|
||||||
select * from api_definition_exec_result
|
select * from api_definition_exec_result
|
||||||
where resource_id = #{resourceId,jdbcType=VARCHAR}
|
where resource_id = #{resourceId,jdbcType=VARCHAR} ORDER BY update_time DESC LIMIT 1
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
|
@ -94,12 +94,24 @@
|
||||||
when 'error' then '未通过'
|
when 'error' then '未通过'
|
||||||
ELSE '未执行' end as status ,
|
ELSE '未执行' end as status ,
|
||||||
CONCAT(FORMAT(SUM(IF(t2.`status` = 'success', 1, 0))/ COUNT(t1.id)*100, 2), '%') passRate
|
CONCAT(FORMAT(SUM(IF(t2.`status` = 'success', 1, 0))/ COUNT(t1.id)*100, 2), '%') passRate
|
||||||
from api_test_case t1 left join api_definition_exec_result t2 on t1.id = t2.resource_id
|
from api_test_case t1 left join (
|
||||||
|
select
|
||||||
|
a.status, a.id, a.resource_id
|
||||||
|
from
|
||||||
|
api_definition_exec_result a
|
||||||
|
left join (
|
||||||
|
select
|
||||||
|
max(start_time) start_time , id, resource_id
|
||||||
|
from
|
||||||
|
api_definition_exec_result
|
||||||
|
group by
|
||||||
|
resource_id ) as b on a.id = b.id
|
||||||
|
where
|
||||||
|
a.start_time = b.start_time)as t2 on t1.id = t2.resource_id
|
||||||
group by t1.api_definition_id having t1.api_definition_id in
|
group by t1.api_definition_id having t1.api_definition_id in
|
||||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||||
#{v}
|
#{v}
|
||||||
</foreach>
|
</foreach>
|
||||||
order by t2.end_time desc;
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<sql id="combine">
|
<sql id="combine">
|
||||||
|
|
|
@ -146,15 +146,45 @@
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="list" resultType="io.metersphere.api.dto.definition.ApiTestCaseResult">
|
<select id="list" resultType="io.metersphere.api.dto.definition.ApiTestCaseResult">
|
||||||
select atc.id, atc.project_id,
|
select
|
||||||
atc.name,atc.priority,atc.api_definition_id,T1.name as createUser ,T2.name as updateUser,
|
atc.id,
|
||||||
atc.description,atc.request,atc.response,atc.create_user_id,
|
atc.project_id,
|
||||||
atc.create_time,atc.update_user_id, atc.update_time,ader.status execResult
|
atc.name,
|
||||||
from api_test_case atc left join user T1 on atc.create_user_id = T1.id left join user T2 on
|
atc.priority,
|
||||||
atc.update_user_id = T2.id left join api_definition_exec_result ader on atc.id = ader.resource_id
|
atc.api_definition_id,
|
||||||
|
u1.name as createUser ,
|
||||||
|
u2.name as updateUser,
|
||||||
|
atc.description,
|
||||||
|
atc.request,
|
||||||
|
atc.response,
|
||||||
|
atc.create_user_id,
|
||||||
|
atc.create_time,
|
||||||
|
atc.update_user_id,
|
||||||
|
atc.update_time,
|
||||||
|
ader.status execResult
|
||||||
|
from
|
||||||
|
api_test_case atc
|
||||||
|
left join user u1 on
|
||||||
|
atc.create_user_id = u1.id
|
||||||
|
left join user u2 on
|
||||||
|
atc.update_user_id = u2.id
|
||||||
|
left join (
|
||||||
|
select
|
||||||
|
a.status, a.id, a.resource_id
|
||||||
|
from
|
||||||
|
api_definition_exec_result a
|
||||||
|
left join (
|
||||||
|
select
|
||||||
|
max(start_time) start_time , id, resource_id
|
||||||
|
from
|
||||||
|
api_definition_exec_result
|
||||||
|
group by
|
||||||
|
resource_id ) as b on a.id = b.id
|
||||||
|
where
|
||||||
|
a.start_time = b.start_time) as ader
|
||||||
|
on atc.id = ader.resource_id
|
||||||
<where>
|
<where>
|
||||||
<if test="request.name != null and request.name!=''">
|
<if test="request.name != null and request.name!=''">
|
||||||
|
|
||||||
and atc.name like CONCAT('%', #{request.name},'%')
|
and atc.name like CONCAT('%', #{request.name},'%')
|
||||||
</if>
|
</if>
|
||||||
<if test="request.id != null and request.id!=''">
|
<if test="request.id != null and request.id!=''">
|
||||||
|
|
|
@ -84,9 +84,4 @@ public class ProjectController {
|
||||||
projectService.updateProject(Project);
|
projectService.updateProject(Project);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/search")
|
|
||||||
public List<ProjectDTO> searchProject(@RequestBody ProjectRequest projectRequest) {
|
|
||||||
projectRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
|
||||||
return projectService.getProjectList(projectRequest);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
FROM alpine:latest
|
|
||||||
LABEL maintainer="support@fit2cloud.com"
|
|
||||||
|
|
||||||
ENV JMETER_VERSION "5.3"
|
|
||||||
ENV KAFKA_BACKEND_LISTENER_VERSION "1.0.4"
|
|
||||||
#定义时区参数
|
|
||||||
ENV TZ=Asia/Shanghai
|
|
||||||
|
|
||||||
RUN apk update && \
|
|
||||||
apk upgrade && \
|
|
||||||
apk add --update openjdk8 wget tar bash && \
|
|
||||||
wget https://mirrors.tuna.tsinghua.edu.cn/apache/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz && \
|
|
||||||
wget https://jmeter-plugins.org/files/packages/jpgc-casutg-2.9.zip && \
|
|
||||||
wget https://jmeter-plugins.org/files/packages/jpgc-tst-2.5.zip && \
|
|
||||||
wget https://github.com/metersphere/jmeter-backend-listener-kafka/releases/download/v${KAFKA_BACKEND_LISTENER_VERSION}/jmeter.backendlistener.kafka-${KAFKA_BACKEND_LISTENER_VERSION}.jar && \
|
|
||||||
wget https://github.com/metersphere/jmeter-plugins-for-apache-dubbo/releases/download/2.7.7/jmeter-plugins-dubbo-2.7.7-jar-with-dependencies.jar && \
|
|
||||||
wget -q "http://search.maven.org/remotecontent?filepath=mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar" -O mysql-connector-java.jar && \
|
|
||||||
wget -q "http://search.maven.org/remotecontent?filepath=com/oracle/database/jdbc/ojdbc8/19.7.0.0/ojdbc8-19.7.0.0.jar" -O ojdbc8.jar && \
|
|
||||||
wget -q "http://search.maven.org/remotecontent?filepath=org/postgresql/postgresql/42.2.14/postgresql-42.2.14.jar" -O postgresql.jar && \
|
|
||||||
wget -q "http://search.maven.org/remotecontent?filepath=com/microsoft/sqlserver/mssql-jdbc/7.4.1.jre8/mssql-jdbc-7.4.1.jre8.jar" -O mssql-jdbc.jar && \
|
|
||||||
mkdir -p /opt/jmeter && \
|
|
||||||
tar -zxf apache-jmeter-${JMETER_VERSION}.tgz -C /opt/jmeter/ --strip-components=1 && \
|
|
||||||
unzip -o jpgc-casutg-2.9.zip -d /tmp/ && mv /tmp/lib/ext/jmeter-plugins-casutg-2.9.jar /opt/jmeter/lib/ext && \
|
|
||||||
unzip -o jpgc-tst-2.5.zip -d /tmp/ && mv /tmp/lib/ext/jmeter-plugins-tst-2.5.jar /opt/jmeter/lib/ext && \
|
|
||||||
mv jmeter.backendlistener.kafka-${KAFKA_BACKEND_LISTENER_VERSION}.jar /opt/jmeter/lib/ext && \
|
|
||||||
mv jmeter-plugins-dubbo-2.7.7-jar-with-dependencies.jar /opt/jmeter/lib/ext && \
|
|
||||||
mv mysql-connector-java.jar /opt/jmeter/lib/ext && \
|
|
||||||
mv ojdbc8.jar /opt/jmeter/lib/ext && \
|
|
||||||
mv postgresql.jar /opt/jmeter/lib/ext && \
|
|
||||||
mv mssql-jdbc.jar /opt/jmeter/lib/ext && \
|
|
||||||
rm -rf apache-jmeter-${JMETER_VERSION}.tgz && \
|
|
||||||
rm -rf jpgc-casutg-2.9.zip && \
|
|
||||||
rm -rf jpgc-tst-2.5.zip && \
|
|
||||||
rm -rf jmeter.backendlistener.kafka-${KAFKA_BACKEND_LISTENER_VERSION}.jar && \
|
|
||||||
rm -rf jmeter-plugins-dubbo-2.7.7-jar-with-dependencies.jar && \
|
|
||||||
rm -rf /var/cache/apk/* && \
|
|
||||||
wget -O /usr/bin/tpl https://github.com/schneidexe/tpl/releases/download/v0.5.0/tpl-linux-amd64 && \
|
|
||||||
chmod +x /usr/bin/tpl && \
|
|
||||||
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo "$TZ" > /etc/timezone
|
|
||||||
|
|
||||||
ENV JMETER_HOME /opt/jmeter
|
|
||||||
ENV PATH $PATH:$JMETER_HOME/bin:/usr/lib/jvm/java-1.8-openjdk/bin
|
|
||||||
|
|
||||||
ADD log4j2.xml $JMETER_HOME/bin/log4j2.xml
|
|
||||||
ADD jmeter.properties $JMETER_HOME/bin/jmeter.properties
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,116 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
|
||||||
this work for additional information regarding copyright ownership.
|
|
||||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
(the "License"); you may not use this file except in compliance with
|
|
||||||
the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
<Configuration status="WARN" packages="org.apache.jmeter.gui.logging">
|
|
||||||
|
|
||||||
<Appenders>
|
|
||||||
|
|
||||||
<File name="jmeter-log" fileName="${sys:jmeter.logfile:-jmeter.log}" append="false">
|
|
||||||
<PatternLayout>
|
|
||||||
<pattern>%d %p %c{1.}: %m%n</pattern>
|
|
||||||
</PatternLayout>
|
|
||||||
</File>
|
|
||||||
|
|
||||||
<GuiLogEvent name="gui-log-event">
|
|
||||||
<PatternLayout>
|
|
||||||
<pattern>%d %p %c{1.}: %m%n</pattern>
|
|
||||||
</PatternLayout>
|
|
||||||
</GuiLogEvent>
|
|
||||||
<Kafka name="Kafka" topic="${env:LOG_TOPIC}">
|
|
||||||
<PatternLayout pattern="${env:REPORT_ID} ${env:RESOURCE_ID} %date %message"/>
|
|
||||||
<Property name="bootstrap.servers">${env:BOOTSTRAP_SERVERS}</Property>
|
|
||||||
</Kafka>
|
|
||||||
</Appenders>
|
|
||||||
|
|
||||||
<Loggers>
|
|
||||||
|
|
||||||
<Root level="info">
|
|
||||||
<AppenderRef ref="jmeter-log" />
|
|
||||||
<AppenderRef ref="gui-log-event" />
|
|
||||||
<AppenderRef ref="Kafka" />
|
|
||||||
</Root>
|
|
||||||
<Logger name="org.apache.kafka" level="INFO" />
|
|
||||||
<Logger name="org.apache.jmeter.junit" level="debug" />
|
|
||||||
<!--
|
|
||||||
<Logger name="org.apache.jmeter.control" level="debug" />
|
|
||||||
<Logger name="org.apache.jmeter.testbeans" level="debug" />
|
|
||||||
<Logger name="org.apache.jmeter.engine" level="debug" />
|
|
||||||
<Logger name="org.apache.jmeter.threads" level="debug" />
|
|
||||||
<Logger name="org.apache.jmeter.gui" level="warn" />
|
|
||||||
<Logger name="org.apache.jmeter.testelement" level="debug" />
|
|
||||||
<Logger name="org.apache.jmeter.util" level="warn" />
|
|
||||||
<Logger name="org.apache.jmeter.protocol.http" level="debug" />
|
|
||||||
-->
|
|
||||||
<!-- # For CookieManager, AuthManager etc: -->
|
|
||||||
<!--
|
|
||||||
<Logger name="org.apache.jmeter.protocol.http.control" level="debug" />
|
|
||||||
<Logger name="org.apache.jmeter.protocol.ftp" level="warn" />
|
|
||||||
<Logger name="org.apache.jmeter.protocol.jdbc" level="debug" />
|
|
||||||
<Logger name="org.apache.jmeter.protocol.java" level="warn" />
|
|
||||||
<Logger name="org.apache.jmeter.testelements.property" level="debug" />
|
|
||||||
-->
|
|
||||||
<Logger name="org.apache.jorphan" level="info" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
# Apache HttpClient logging examples
|
|
||||||
-->
|
|
||||||
<!-- # Enable header wire + context logging - Best for Debugging -->
|
|
||||||
<!--
|
|
||||||
<Logger name="org.apache.http" level="debug" />
|
|
||||||
<Logger name="org.apache.http.wire" level="error" />
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- # Enable full wire + context logging -->
|
|
||||||
<!-- <Logger name="org.apache.http" level="debug" /> -->
|
|
||||||
|
|
||||||
<!-- # Enable context logging for connection management -->
|
|
||||||
<!-- <Logger name="org.apache.http.impl.conn" level="debug" /> -->
|
|
||||||
|
|
||||||
<!-- # Enable context logging for connection management / request execution -->
|
|
||||||
<!--
|
|
||||||
<Logger name="org.apache.http.impl.conn" level="debug" />
|
|
||||||
<Logger name="org.apache.http.impl.client" level="debug" />
|
|
||||||
<Logger name="org.apache.http.client" level="debug" />
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
# Reporting logging configuration examples
|
|
||||||
-->
|
|
||||||
<!-- # If you want to debug reporting, uncomment this line -->
|
|
||||||
<!-- <Logger name="org.apache.jmeter.report" level="debug" /> -->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
# More user specific logging configuration examples.
|
|
||||||
-->
|
|
||||||
<!-- <Logger name="org.apache.jorphan.reflect" level="debug" /> -->
|
|
||||||
<!--
|
|
||||||
# Warning: Enabling the next debug line causes javax.net.ssl.SSLException: Received fatal alert: unexpected_message
|
|
||||||
for certain sites when used with the default HTTP Sampler
|
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
<Logger name="org.apache.jmeter.util.HttpSSLProtocolSocketFactory" level="debug" />
|
|
||||||
<Logger name="org.apache.jmeter.util.JsseSSLManager" level="debug" />
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
# Enable Proxy request debug
|
|
||||||
-->
|
|
||||||
<!-- <Logger name="org.apache.jmeter.protocol.http.proxy.HttpRequestHdr" level="debug" /> -->
|
|
||||||
|
|
||||||
</Loggers>
|
|
||||||
|
|
||||||
</Configuration>
|
|
|
@ -1,14 +0,0 @@
|
||||||
FROM registry.fit2cloud.com/metersphere/jmeter-base:0.0.1
|
|
||||||
LABEL maintainer="support@fit2cloud.com"
|
|
||||||
|
|
||||||
EXPOSE 60000
|
|
||||||
ENV SSL_DISABLED true
|
|
||||||
ENV TESTS_DIR /test
|
|
||||||
|
|
||||||
ADD run-test.sh /run-test.sh
|
|
||||||
RUN chmod +x /run-test.sh \
|
|
||||||
&& mkdir /test \
|
|
||||||
&& mkdir /jmeter-log
|
|
||||||
|
|
||||||
WORKDIR /jmeter-log/
|
|
||||||
ENTRYPOINT /run-test.sh
|
|
|
@ -1,3 +0,0 @@
|
||||||
for file in ${TESTS_DIR}/*.jmx; do
|
|
||||||
jmeter -n -t ${file} -Jserver.rmi.ssl.disable=${SSL_DISABLED}
|
|
||||||
done
|
|
|
@ -17,15 +17,21 @@
|
||||||
|
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-tooltip effect="dark" :content="request.responseResult.responseCode" placement="bottom" :open-delay="800">
|
<el-tooltip effect="dark" :content="request.responseResult.responseCode" placement="bottom" :open-delay="800">
|
||||||
<div class="url" style="color: #5daf34">{{ request.responseResult.responseCode }}</div>
|
<div style="color: #5daf34" v-if="request.success">{{ request.responseResult.responseCode }}</div>
|
||||||
|
<div style="color: #FE6F71" v-else>{{ request.responseResult.responseCode }}</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="3">
|
<el-col :span="3">
|
||||||
|
<span v-if="request.success">
|
||||||
{{request.responseResult.responseTime}} ms
|
{{request.responseResult.responseTime}} ms
|
||||||
|
</span>
|
||||||
|
<span style="color: #FE6F71" v-else>
|
||||||
|
{{request.responseResult.responseTime}} ms
|
||||||
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="2">
|
<el-col :span="2">
|
||||||
<div class="success">
|
<div>
|
||||||
<el-tag size="mini" type="success" v-if="request.success">
|
<el-tag size="mini" type="success" v-if="request.success">
|
||||||
{{ $t('api_report.success') }}
|
{{ $t('api_report.success') }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
|
|
@ -8,33 +8,10 @@
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="10">
|
<el-col :span="10">
|
||||||
<div class="name">{{request.name}}</div>
|
|
||||||
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
|
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
|
||||||
<div class="url">{{request.url}}</div>
|
<div class="url">{{request.url}}</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4">
|
|
||||||
{{request.startTime | timestampFormatDate(true) }}
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="2">
|
|
||||||
<div class="time">
|
|
||||||
{{request.responseResult.responseTime}}
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="2">
|
|
||||||
{{request.error}}
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="2">
|
|
||||||
{{assertion}}
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="2">
|
|
||||||
<el-tag size="mini" type="success" v-if="request.success">
|
|
||||||
{{$t('api_report.success')}}
|
|
||||||
</el-tag>
|
|
||||||
<el-tag size="mini" type="danger" v-else>
|
|
||||||
{{$t('api_report.fail')}}
|
|
||||||
</el-tag>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<el-collapse-transition>
|
<el-collapse-transition>
|
||||||
|
@ -52,7 +29,6 @@
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<ms-request-metric :request="request"/>
|
|
||||||
<ms-request-text v-if="isCodeEditAlive" :request="request"/>
|
<ms-request-text v-if="isCodeEditAlive" :request="request"/>
|
||||||
<br>
|
<br>
|
||||||
<ms-response-text :request-type="requestType" v-if="isCodeEditAlive" :response="request.responseResult"/>
|
<ms-response-text :request-type="requestType" v-if="isCodeEditAlive" :response="request.responseResult"/>
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
methods: {
|
methods: {
|
||||||
setFiles(item, bodyUploadFiles, obj) {
|
setFiles(item, bodyUploadFiles, obj) {
|
||||||
if (item.body) {
|
if (item.body) {
|
||||||
|
if (item.body.kvs) {
|
||||||
item.body.kvs.forEach(param => {
|
item.body.kvs.forEach(param => {
|
||||||
if (param.files) {
|
if (param.files) {
|
||||||
param.files.forEach(item => {
|
param.files.forEach(item => {
|
||||||
|
@ -47,6 +48,8 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
if (item.body.binary) {
|
||||||
item.body.binary.forEach(param => {
|
item.body.binary.forEach(param => {
|
||||||
if (param.files) {
|
if (param.files) {
|
||||||
param.files.forEach(item => {
|
param.files.forEach(item => {
|
||||||
|
@ -63,6 +66,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
recursiveFile(arr, bodyUploadFiles, obj) {
|
recursiveFile(arr, bodyUploadFiles, obj) {
|
||||||
arr.forEach(item => {
|
arr.forEach(item => {
|
||||||
|
|
|
@ -113,7 +113,7 @@
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<!-- 场景步骤-->
|
<!-- 场景步骤-->
|
||||||
<div v-loading="isReloadData">
|
<div v-loading="loading">
|
||||||
<p class="tip">{{$t('api_test.automation.scenario_step')}} </p>
|
<p class="tip">{{$t('api_test.automation.scenario_step')}} </p>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="21">
|
<el-col :span="21">
|
||||||
|
@ -157,13 +157,13 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<!-- 场景步骤内容 -->
|
<!-- 场景步骤内容 -->
|
||||||
<div style="margin-top: 10px" v-loading="isReloadData">
|
<div style="margin-top: 10px" v-loading="loading">
|
||||||
<el-tree node-key="resourceId" :props="props" :data="scenarioDefinition"
|
<el-tree node-key="resourceId" :props="props" :data="scenarioDefinition"
|
||||||
:default-expanded-keys="expandedNode"
|
:default-expanded-keys="expandedNode"
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
@node-expand="nodeExpand"
|
@node-expand="nodeExpand"
|
||||||
@node-collapse="nodeCollapse"
|
@node-collapse="nodeCollapse"
|
||||||
:allow-drop="allowDrop" @node-drag-end="allowDrag" @node-click="nodeClick" v-if="!isReloadData" draggable>
|
:allow-drop="allowDrop" @node-drag-end="allowDrag" @node-click="nodeClick" v-if="!loading" draggable>
|
||||||
<span class="custom-tree-node father" slot-scope="{ node, data}" style="width: 96%">
|
<span class="custom-tree-node father" slot-scope="{ node, data}" style="width: 96%">
|
||||||
<template>
|
<template>
|
||||||
<!-- 场景 -->
|
<!-- 场景 -->
|
||||||
|
@ -267,6 +267,8 @@
|
||||||
|
|
||||||
<!--场景公共参数-->
|
<!--场景公共参数-->
|
||||||
<ms-scenario-parameters :currentScenario="currentScenario" @addParameters="addParameters" ref="scenarioParameters"/>
|
<ms-scenario-parameters :currentScenario="currentScenario" @addParameters="addParameters" ref="scenarioParameters"/>
|
||||||
|
<!--外部导入-->
|
||||||
|
<api-import ref="apiImport" :saved="false" @refresh="apiImport"/>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
@ -293,6 +295,7 @@
|
||||||
import MsApiScenarioComponent from "./ApiScenarioComponent";
|
import MsApiScenarioComponent from "./ApiScenarioComponent";
|
||||||
import MsApiReportDetail from "../report/ApiReportDetail";
|
import MsApiReportDetail from "../report/ApiReportDetail";
|
||||||
import MsScenarioParameters from "./ScenarioParameters";
|
import MsScenarioParameters from "./ScenarioParameters";
|
||||||
|
import ApiImport from "../../definition/components/import/ApiImport";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "EditApiScenario",
|
name: "EditApiScenario",
|
||||||
|
@ -301,13 +304,21 @@
|
||||||
currentScenario: {},
|
currentScenario: {},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
ApiEnvironmentConfig, MsScenarioParameters,
|
ApiEnvironmentConfig,
|
||||||
MsApiReportDetail, MsAddTag, MsRun,
|
MsScenarioParameters,
|
||||||
MsApiScenarioComponent, MsImportApiScenario,
|
MsApiReportDetail,
|
||||||
MsJsr233Processor, MsConstantTimer,
|
MsAddTag, MsRun,
|
||||||
MsIfController, MsApiAssertions,
|
MsApiScenarioComponent,
|
||||||
MsApiExtract, MsApiDefinition,
|
MsImportApiScenario,
|
||||||
MsApiComponent, MsApiCustomize
|
MsJsr233Processor,
|
||||||
|
MsConstantTimer,
|
||||||
|
MsIfController,
|
||||||
|
MsApiAssertions,
|
||||||
|
MsApiExtract,
|
||||||
|
MsApiDefinition,
|
||||||
|
MsApiComponent,
|
||||||
|
MsApiCustomize,
|
||||||
|
ApiImport,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -333,7 +344,7 @@
|
||||||
options: API_STATUS,
|
options: API_STATUS,
|
||||||
levels: PRIORITY,
|
levels: PRIORITY,
|
||||||
scenario: {},
|
scenario: {},
|
||||||
isReloadData: false,
|
loading: false,
|
||||||
apiListVisible: false,
|
apiListVisible: false,
|
||||||
customizeVisible: false,
|
customizeVisible: false,
|
||||||
scenarioVisible: false,
|
scenarioVisible: false,
|
||||||
|
@ -402,6 +413,7 @@
|
||||||
this.scenarioVisible = true;
|
this.scenarioVisible = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
this.$refs.apiImport.open();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.sort();
|
this.sort();
|
||||||
|
@ -553,9 +565,9 @@
|
||||||
this.reload();
|
this.reload();
|
||||||
},
|
},
|
||||||
reload() {
|
reload() {
|
||||||
this.isReloadData = true
|
this.loading = true
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.isReloadData = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
runDebug() {
|
runDebug() {
|
||||||
|
@ -622,6 +634,7 @@
|
||||||
},
|
},
|
||||||
setFiles(item, bodyUploadFiles, obj) {
|
setFiles(item, bodyUploadFiles, obj) {
|
||||||
if (item.body) {
|
if (item.body) {
|
||||||
|
if (item.body.kvs) {
|
||||||
item.body.kvs.forEach(param => {
|
item.body.kvs.forEach(param => {
|
||||||
if (param.files) {
|
if (param.files) {
|
||||||
param.files.forEach(item => {
|
param.files.forEach(item => {
|
||||||
|
@ -637,6 +650,8 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
if (item.body.binary) {
|
||||||
item.body.binary.forEach(param => {
|
item.body.binary.forEach(param => {
|
||||||
if (param.files) {
|
if (param.files) {
|
||||||
param.files.forEach(item => {
|
param.files.forEach(item => {
|
||||||
|
@ -653,6 +668,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
recursiveFile(arr, bodyUploadFiles, obj) {
|
recursiveFile(arr, bodyUploadFiles, obj) {
|
||||||
arr.forEach(item => {
|
arr.forEach(item => {
|
||||||
|
@ -728,7 +744,7 @@
|
||||||
},
|
},
|
||||||
runRefresh() {
|
runRefresh() {
|
||||||
this.debugVisible = true;
|
this.debugVisible = true;
|
||||||
this.isReloadData = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
showScenarioParameters() {
|
showScenarioParameters() {
|
||||||
this.$refs.scenarioParameters.open(this.currentScenario.variables);
|
this.$refs.scenarioParameters.open(this.currentScenario.variables);
|
||||||
|
@ -736,6 +752,15 @@
|
||||||
addParameters(data) {
|
addParameters(data) {
|
||||||
this.currentScenario.variables = data;
|
this.currentScenario.variables = data;
|
||||||
this.reload();
|
this.reload();
|
||||||
|
},
|
||||||
|
apiImport(importData) {
|
||||||
|
if (importData && importData.data) {
|
||||||
|
importData.data.forEach(item => {
|
||||||
|
this.setApiParameter(item, "API", "Copy");
|
||||||
|
})
|
||||||
|
this.sort();
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,7 +237,7 @@
|
||||||
if (!this.$refs.apiList[0].tableData) {
|
if (!this.$refs.apiList[0].tableData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let obj = {protocol: this.currentProtocol, data: this.$refs.apiList[0].tableData}
|
let obj = {projectName: getCurrentProjectID(), protocol: this.currentProtocol, data: this.$refs.apiList[0].tableData}
|
||||||
downloadFile("导出API.json", JSON.stringify(obj));
|
downloadFile("导出API.json", JSON.stringify(obj));
|
||||||
},
|
},
|
||||||
refresh(data) {
|
refresh(data) {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog
|
||||||
|
:title="$t('commons.batch_add')"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="60%"
|
||||||
|
class="batch-edit-dialog"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
@close="handleClose">
|
||||||
|
<div>
|
||||||
|
<div>格式:参数名,必填,参数值,备注 如:Accept-Encoding,必填,utf-8,编码</div>
|
||||||
|
<div style="height: 200px">
|
||||||
|
<ms-code-edit :enable-format="false" mode="text" :data.sync="parameters" theme="eclipse" :modes="['text']"
|
||||||
|
ref="codeEdit"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template v-slot:footer>
|
||||||
|
<ms-dialog-footer
|
||||||
|
@cancel="dialogVisible = false"
|
||||||
|
@confirm="confirm()"/>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||||
|
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
||||||
|
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "BatchAddParameter",
|
||||||
|
components: {
|
||||||
|
MsDialogFooter,
|
||||||
|
MsCodeEdit
|
||||||
|
},
|
||||||
|
props: {},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
parameters: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.dialogVisible = true;
|
||||||
|
listenGoBack(this.handleClose);
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.parameters = "";
|
||||||
|
removeGoBackListener(this.handleClose);
|
||||||
|
},
|
||||||
|
confirm() {
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.$emit("batchSave", this.parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -25,6 +25,10 @@
|
||||||
{{ $t('api_test.definition.request.body_binary') }}
|
{{ $t('api_test.definition.request.body_binary') }}
|
||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
|
<el-row v-if="body.type == 'Form Data' || body.type == 'WWW_FORM'">
|
||||||
|
<el-link class="ms-el-link" @click="batchAdd"> {{$t("commons.batch_add")}}</el-link>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<ms-api-variable :is-read-only="isReadOnly"
|
<ms-api-variable :is-read-only="isReadOnly"
|
||||||
:parameters="body.kvs"
|
:parameters="body.kvs"
|
||||||
:isShowEnable="isShowEnable"
|
:isShowEnable="isShowEnable"
|
||||||
|
@ -55,12 +59,14 @@
|
||||||
type="body"
|
type="body"
|
||||||
v-if="body.type == 'BINARY'"/>
|
v-if="body.type == 'BINARY'"/>
|
||||||
|
|
||||||
|
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsApiKeyValue from "../ApiKeyValue";
|
import MsApiKeyValue from "../ApiKeyValue";
|
||||||
import {BODY_FORMAT, BODY_TYPE, KeyValue} from "../../model/ApiTestModel";
|
import {BODY_TYPE, KeyValue} from "../../model/ApiTestModel";
|
||||||
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
|
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
|
||||||
import MsJsonCodeEdit from "../../../../common/components/MsJsonCodeEdit";
|
import MsJsonCodeEdit from "../../../../common/components/MsJsonCodeEdit";
|
||||||
|
|
||||||
|
@ -68,6 +74,7 @@
|
||||||
import MsApiVariable from "../ApiVariable";
|
import MsApiVariable from "../ApiVariable";
|
||||||
import MsApiBinaryVariable from "./ApiBinaryVariable";
|
import MsApiBinaryVariable from "./ApiBinaryVariable";
|
||||||
import MsApiFromUrlVariable from "./ApiFromUrlVariable";
|
import MsApiFromUrlVariable from "./ApiFromUrlVariable";
|
||||||
|
import BatchAddParameter from "../basis/BatchAddParameter";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiBody",
|
name: "MsApiBody",
|
||||||
|
@ -78,7 +85,8 @@
|
||||||
MsApiKeyValue,
|
MsApiKeyValue,
|
||||||
MsApiBinaryVariable,
|
MsApiBinaryVariable,
|
||||||
MsApiFromUrlVariable,
|
MsApiFromUrlVariable,
|
||||||
MsJsonCodeEdit
|
MsJsonCodeEdit,
|
||||||
|
BatchAddParameter
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
body: {},
|
body: {},
|
||||||
|
@ -147,9 +155,29 @@
|
||||||
},
|
},
|
||||||
jsonError(e) {
|
jsonError(e) {
|
||||||
this.$error(e);
|
this.$error(e);
|
||||||
|
},
|
||||||
|
batchAdd() {
|
||||||
|
this.$refs.batchAddParameter.open();
|
||||||
|
},
|
||||||
|
batchSave(data) {
|
||||||
|
if (data) {
|
||||||
|
let params = data.split("\n");
|
||||||
|
let keyValues = [];
|
||||||
|
params.forEach(item => {
|
||||||
|
let line = item.split(/,|,/);
|
||||||
|
let required = false;
|
||||||
|
if (line[1] === '必填' || line[1] === 'true') {
|
||||||
|
required = true;
|
||||||
|
}
|
||||||
|
keyValues.push(new KeyValue({name: line[0], required: required, value: line[2], description: line[3], type: "text", valid: false, file: false, encode: true, enable: true, contentType: "text/plain"}));
|
||||||
|
})
|
||||||
|
keyValues.forEach(item => {
|
||||||
|
this.body.kvs.unshift(item);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
if (!this.body.type) {
|
if (!this.body.type) {
|
||||||
this.body.type = BODY_TYPE.FORM_DATA;
|
this.body.type = BODY_TYPE.FORM_DATA;
|
||||||
|
@ -187,4 +215,8 @@
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ms-el-link {
|
||||||
|
float: right;
|
||||||
|
margin-right: 45px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -70,6 +70,12 @@
|
||||||
export default {
|
export default {
|
||||||
name: "ApiImport",
|
name: "ApiImport",
|
||||||
components: {MsDialogFooter},
|
components: {MsDialogFooter},
|
||||||
|
props: {
|
||||||
|
saved: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
|
@ -148,7 +154,7 @@
|
||||||
},
|
},
|
||||||
uploadValidate(file, fileList) {
|
uploadValidate(file, fileList) {
|
||||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||||
if (!this.selectedPlatform.suffixes.has(suffix)) {
|
if (this.selectedPlatform.suffixes && !this.selectedPlatform.suffixes.has(suffix)) {
|
||||||
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
|
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +176,7 @@
|
||||||
let res = response.data;
|
let res = response.data;
|
||||||
this.$success(this.$t('test_track.case.import.success'));
|
this.$success(this.$t('test_track.case.import.success'));
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.$emit('refresh');
|
this.$emit('refresh', res);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -181,8 +187,11 @@
|
||||||
let param = {};
|
let param = {};
|
||||||
Object.assign(param, this.formData);
|
Object.assign(param, this.formData);
|
||||||
param.platform = this.selectedPlatformValue;
|
param.platform = this.selectedPlatformValue;
|
||||||
|
param.saved = this.saved;
|
||||||
|
if (this.currentModule) {
|
||||||
param.moduleId = this.currentModule.id;
|
param.moduleId = this.currentModule.id;
|
||||||
param.modulePath = this.currentModule.path;
|
param.modulePath = this.currentModule.path;
|
||||||
|
}
|
||||||
param.projectId = getCurrentProjectID();
|
param.projectId = getCurrentProjectID();
|
||||||
if (!this.swaggerUrlEable) {
|
if (!this.swaggerUrlEable) {
|
||||||
param.swaggerUrl = undefined;
|
param.swaggerUrl = undefined;
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
||||||
<ms-api-key-value :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers"/>
|
<ms-api-key-value :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
@ -25,7 +24,9 @@
|
||||||
<div class="el-step__icon-inner">{{request.arguments.length-1}}</div>
|
<div class="el-step__icon-inner">{{request.arguments.length-1}}</div>
|
||||||
</div></span>
|
</div></span>
|
||||||
</el-tooltip>
|
</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"/>
|
<ms-api-variable :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.arguments"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
@ -39,6 +40,9 @@
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</el-tooltip>
|
</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"/>
|
<ms-api-variable :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.rest"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
@ -71,6 +75,9 @@
|
||||||
<!--提取规则-->
|
<!--提取规则-->
|
||||||
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
|
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<!--操作按钮-->
|
<!--操作按钮-->
|
||||||
|
@ -98,15 +105,24 @@
|
||||||
import {createComponent} from "../../jmeter/components";
|
import {createComponent} from "../../jmeter/components";
|
||||||
import MsApiAssertions from "../../assertion/ApiAssertions";
|
import MsApiAssertions from "../../assertion/ApiAssertions";
|
||||||
import MsApiExtract from "../../extract/ApiExtract";
|
import MsApiExtract from "../../extract/ApiExtract";
|
||||||
import {Assertions, Body, Extract} from "../../../model/ApiTestModel";
|
import {Assertions, Body, Extract, KeyValue} from "../../../model/ApiTestModel";
|
||||||
import {getUUID} from "@/common/js/utils";
|
import {getUUID} from "@/common/js/utils";
|
||||||
|
import BatchAddParameter from "../../basis/BatchAddParameter";
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiHttpRequestForm",
|
name: "MsApiHttpRequestForm",
|
||||||
components: {
|
components: {
|
||||||
MsJsr233Processor,
|
MsJsr233Processor,
|
||||||
MsApiAdvancedConfig,
|
MsApiAdvancedConfig,
|
||||||
MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAuthConfig, MsApiBody, MsApiKeyValue, MsApiAssertions
|
BatchAddParameter,
|
||||||
|
MsApiVariable,
|
||||||
|
ApiRequestMethodSelect,
|
||||||
|
MsApiExtract,
|
||||||
|
MsApiAuthConfig,
|
||||||
|
MsApiBody,
|
||||||
|
MsApiKeyValue,
|
||||||
|
MsApiAssertions
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
request: {},
|
request: {},
|
||||||
|
@ -149,7 +165,7 @@
|
||||||
},
|
},
|
||||||
headerSuggestions: REQUEST_HEADERS,
|
headerSuggestions: REQUEST_HEADERS,
|
||||||
isReloadData: false,
|
isReloadData: false,
|
||||||
isBodyShow: true
|
isBodyShow: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -216,6 +232,35 @@
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.isBodyShow = true;
|
this.isBodyShow = true;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
batchAdd() {
|
||||||
|
this.$refs.batchAddParameter.open();
|
||||||
|
},
|
||||||
|
batchSave(data) {
|
||||||
|
if (data) {
|
||||||
|
let params = data.split("\n");
|
||||||
|
let keyValues = [];
|
||||||
|
params.forEach(item => {
|
||||||
|
let line = item.split(/,|,/);
|
||||||
|
let required = false;
|
||||||
|
if (line[1] === '必填' || line[1] === 'true') {
|
||||||
|
required = true;
|
||||||
|
}
|
||||||
|
keyValues.push(new KeyValue({name: line[0], required: required, value: line[2], description: line[3], type: "text", valid: false, file: false, encode: true, enable: true, contentType: "text/plain"}));
|
||||||
|
})
|
||||||
|
keyValues.forEach(item => {
|
||||||
|
switch (this.activeName) {
|
||||||
|
case "parameters":
|
||||||
|
this.request.arguments.unshift(item);
|
||||||
|
break;
|
||||||
|
case "rest":
|
||||||
|
this.request.rest.unshift(item);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,4 +318,8 @@
|
||||||
border: #E6EEF2;
|
border: #E6EEF2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ms-el-link {
|
||||||
|
float: right;
|
||||||
|
margin-right: 45px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,25 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="menu-bar" v-if="isRouterAlive">
|
<div id="menu-bar" v-if="isRouterAlive">
|
||||||
<el-row type="flex">
|
<el-row type="flex">
|
||||||
|
<project-change :project-name="currentProject"/>
|
||||||
<el-col :span="14">
|
<el-col :span="14">
|
||||||
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router :default-active='$route.path'>
|
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router :default-active='$route.path'>
|
||||||
|
|
||||||
<el-submenu :class="{'deactivation':!isProjectActivation}"
|
|
||||||
v-permission="['test_manager','test_user','test_viewer']" index="3">
|
|
||||||
<template v-slot:title>
|
|
||||||
<span style="display: inline-block;width: 150px;white-space:nowrap; overflow:hidden; text-overflow:ellipsis;" :title="currentProject">
|
|
||||||
{{ $t('commons.project') }}: {{currentProject}}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<search-list ref="projectRecent" :options="projectRecent" :current-project.sync="currentProject"/>
|
|
||||||
<el-divider class="menu-divider"/>
|
|
||||||
<el-menu-item :index="'/setting/project/create'">
|
|
||||||
<font-awesome-icon :icon="['fa', 'plus']"/>
|
|
||||||
<span style="padding-left: 7px;">{{ $t("project.create") }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<ms-show-all :index="'/setting/project/all'"/>
|
|
||||||
</el-submenu>
|
|
||||||
|
|
||||||
<el-menu-item :index="'/api/home'">
|
<el-menu-item :index="'/api/home'">
|
||||||
{{ $t("i18n.home") }}
|
{{ $t("i18n.home") }}
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
@ -75,22 +60,13 @@ import MsCreateButton from "../../common/head/CreateButton";
|
||||||
import MsCreateTest from "../../common/head/CreateTest";
|
import MsCreateTest from "../../common/head/CreateTest";
|
||||||
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
|
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
|
||||||
import SearchList from "@/business/components/common/head/SearchList";
|
import SearchList from "@/business/components/common/head/SearchList";
|
||||||
|
import ProjectChange from "@/business/components/common/head/ProjectSwitch";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiHeaderMenus",
|
name: "MsApiHeaderMenus",
|
||||||
components: {SearchList, MsCreateTest, MsCreateButton, MsShowAll, MsRecentList},
|
components: {SearchList, MsCreateTest, MsCreateButton, MsShowAll, MsRecentList, ProjectChange},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
projectRecent: {
|
|
||||||
title: this.$t('project.recent'),
|
|
||||||
url: "/project/recent/5",
|
|
||||||
index: function (item) {
|
|
||||||
return '/api/test/list/' + item.id;
|
|
||||||
},
|
|
||||||
router: function (item) {
|
|
||||||
return {name: 'ApiTestList', params: {projectId: item.id, projectName: item.name}}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
testRecent: {
|
testRecent: {
|
||||||
title: this.$t('load_test.recent'),
|
title: this.$t('load_test.recent'),
|
||||||
url: "/api/recent/5",
|
url: "/api/recent/5",
|
||||||
|
@ -115,19 +91,10 @@ export default {
|
||||||
currentProject: ''
|
currentProject: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// watch: {
|
|
||||||
// '$route'(to) {
|
|
||||||
// this.init();
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
methods: {
|
methods: {
|
||||||
registerEvents() {
|
registerEvents() {
|
||||||
ApiEvent.$on(LIST_CHANGE, () => {
|
ApiEvent.$on(LIST_CHANGE, () => {
|
||||||
// todo 这里偶尔会有 refs 为空的情况
|
// // todo 这里偶尔会有 refs 为空的情况
|
||||||
if (!this.$refs.projectRecent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$refs.projectRecent.recent();
|
|
||||||
this.$refs.testRecent.recent();
|
this.$refs.testRecent.recent();
|
||||||
this.$refs.reportRecent.recent();
|
this.$refs.reportRecent.recent();
|
||||||
});
|
});
|
||||||
|
@ -138,17 +105,6 @@ export default {
|
||||||
this.isRouterAlive = true;
|
this.isRouterAlive = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// init() {
|
|
||||||
// let path = this.$route.path;
|
|
||||||
// if (path.indexOf("/api/test/list") >= 0 && !!this.$route.params.projectId) {
|
|
||||||
// this.apiTestProjectPath = path;
|
|
||||||
// //不激活项目菜单栏
|
|
||||||
// this.isProjectActivation = false;
|
|
||||||
// this.reload();
|
|
||||||
// } else {
|
|
||||||
// this.isProjectActivation = true;
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.registerEvents();
|
this.registerEvents();
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
<template>
|
|
||||||
<span>
|
|
||||||
<el-submenu index="10">
|
|
||||||
<template v-slot:title>操作</template>
|
|
||||||
<el-input
|
|
||||||
placeholder="请输入内容"
|
|
||||||
prefix-icon="el-icon-search"
|
|
||||||
v-model="input2">
|
|
||||||
</el-input>
|
|
||||||
</el-submenu>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "ProjectMenu",
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
input2: '1'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
<template>
|
||||||
|
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" default-active="1" router>
|
||||||
|
<!-- 不激活项目路由-->
|
||||||
|
<el-menu-item index="1" v-show="false">Placeholder</el-menu-item>
|
||||||
|
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="2" popper-class="submenu">
|
||||||
|
<template v-slot:title>
|
||||||
|
<span class="project-name" :title="currentProject">
|
||||||
|
{{ $t('commons.project') }}: {{currentProject}}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<search-list :current-project.sync="currentProject"/>
|
||||||
|
<el-divider/>
|
||||||
|
<el-menu-item :index="'/setting/project/create'">
|
||||||
|
<font-awesome-icon :icon="['fa', 'plus']"/>
|
||||||
|
<span style="padding-left: 7px;">{{ $t("project.create") }}</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item :index="'/setting/project/all'">
|
||||||
|
<font-awesome-icon :icon="['fa', 'list-ul']"/>
|
||||||
|
<span style="padding-left: 7px;">{{ $t('commons.show_all') }}</span>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
|
</el-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SearchList from "@/business/components/common/head/SearchList";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ProjectSwitch",
|
||||||
|
props: {
|
||||||
|
projectName: String
|
||||||
|
},
|
||||||
|
components: {SearchList},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentProject: this.projectName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.project-name {
|
||||||
|
display: inline-block;
|
||||||
|
width: 130px;
|
||||||
|
white-space:nowrap;
|
||||||
|
overflow:hidden;
|
||||||
|
text-overflow:ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-divider--horizontal {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="result.loading" class="search-list">
|
<div v-loading="result.loading">
|
||||||
<el-input placeholder="搜索项目"
|
<el-input placeholder="搜索项目"
|
||||||
prefix-icon="el-icon-search"
|
prefix-icon="el-icon-search"
|
||||||
v-model="searchString"
|
v-model="searchString"
|
||||||
|
@ -76,13 +76,6 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
search() {
|
|
||||||
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
|
|
||||||
this.result = this.$post("/project/search", {name: this.searchString},response => {
|
|
||||||
this.items = response.data;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
query(queryString) {
|
query(queryString) {
|
||||||
this.items = queryString ? this.searchArray.filter(this.createFilter(queryString)) : this.searchArray;
|
this.items = queryString ? this.searchArray.filter(this.createFilter(queryString)) : this.searchArray;
|
||||||
},
|
},
|
||||||
|
@ -98,13 +91,10 @@ export default {
|
||||||
}
|
}
|
||||||
this.$post("/user/update/current", {id: this.userId, lastProjectId: projectId}, () => {
|
this.$post("/user/update/current", {id: this.userId, lastProjectId: projectId}, () => {
|
||||||
localStorage.setItem(PROJECT_ID, projectId);
|
localStorage.setItem(PROJECT_ID, projectId);
|
||||||
if (this.$route.path.indexOf('/track/review/view/') >= 0) {
|
let path = this.$route.matched[0].path ? this.$route.matched[0].path : '/';
|
||||||
this.$router.replace('/track/review/all');
|
this.$router.push(path).then(() => {
|
||||||
} else if (this.$route.path.indexOf('/track/plan/view/') >= 0) {
|
window.location.reload()
|
||||||
this.$router.replace('/track/plan/all');
|
}).catch(err => err);
|
||||||
} else {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
this.changeProjectName(projectId);
|
this.changeProjectName(projectId);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -135,7 +125,7 @@ export default {
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-left: 20px;
|
padding-left: 15px;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -1,25 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="menu-bar">
|
<div id="menu-bar">
|
||||||
<el-row type="flex">
|
<el-row type="flex">
|
||||||
<el-col :span="10">
|
<project-change :project-name="currentProject"/>
|
||||||
|
<el-col :span="12">
|
||||||
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router :default-active='$route.path'>
|
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router :default-active='$route.path'>
|
||||||
|
|
||||||
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
|
|
||||||
index="3" popper-class="submenu">
|
|
||||||
<template v-slot:title>
|
|
||||||
<span style="display: inline-block;width: 150px;white-space:nowrap; overflow:hidden; text-overflow:ellipsis;" :title="currentProject">
|
|
||||||
{{ $t('commons.project') }}: {{currentProject}}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<search-list ref="projectRecent" :options="projectRecent" :current-project.sync="currentProject"/>
|
|
||||||
<el-divider/>
|
|
||||||
<el-menu-item :index="'/setting/project/create'">
|
|
||||||
<font-awesome-icon :icon="['fa', 'plus']"/>
|
|
||||||
<span style="padding-left: 7px;">{{ $t("project.create") }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<ms-show-all :index="'/setting/project/all'"/>
|
|
||||||
</el-submenu>
|
|
||||||
|
|
||||||
<el-menu-item :index="'/performance/home'">
|
<el-menu-item :index="'/performance/home'">
|
||||||
{{ $t("i18n.home") }}
|
{{ $t("i18n.home") }}
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
@ -61,10 +45,12 @@ import MsCreateButton from "../../common/head/CreateButton";
|
||||||
import MsShowAll from "../../common/head/ShowAll";
|
import MsShowAll from "../../common/head/ShowAll";
|
||||||
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
|
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
|
||||||
import SearchList from "@/business/components/common/head/SearchList";
|
import SearchList from "@/business/components/common/head/SearchList";
|
||||||
|
import ProjectChange from "@/business/components/common/head/ProjectSwitch";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "PerformanceHeaderMenus",
|
name: "PerformanceHeaderMenus",
|
||||||
components: {
|
components: {
|
||||||
|
ProjectChange,
|
||||||
SearchList,
|
SearchList,
|
||||||
MsCreateButton,
|
MsCreateButton,
|
||||||
MsShowAll,
|
MsShowAll,
|
||||||
|
@ -73,16 +59,6 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
projectRecent: {
|
|
||||||
title: this.$t('project.recent'),
|
|
||||||
url: "/project/recent/5",
|
|
||||||
index(item) {
|
|
||||||
return '/performance/test/' + item.id;
|
|
||||||
},
|
|
||||||
router(item) {
|
|
||||||
return {name: 'perPlan', params: {projectId: item.id, projectName: item.name}}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
testRecent: {
|
testRecent: {
|
||||||
title: this.$t('load_test.recent'),
|
title: this.$t('load_test.recent'),
|
||||||
url: "/performance/recent/5",
|
url: "/performance/recent/5",
|
||||||
|
@ -108,11 +84,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
registerEvents() {
|
registerEvents() {
|
||||||
PerformanceEvent.$on(LIST_CHANGE, () => {
|
PerformanceEvent.$on(LIST_CHANGE, () => {
|
||||||
// todo 这里偶尔会有 refs 为空的情况
|
// // todo 这里偶尔会有 refs 为空的情况
|
||||||
if (!this.$refs.projectRecent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$refs.projectRecent.recent();
|
|
||||||
this.$refs.testRecent.recent();
|
this.$refs.testRecent.recent();
|
||||||
this.$refs.reportRecent.recent();
|
this.$refs.reportRecent.recent();
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,25 +2,10 @@
|
||||||
|
|
||||||
<div id="menu-bar" v-if="isRouterAlive">
|
<div id="menu-bar" v-if="isRouterAlive">
|
||||||
<el-row type="flex">
|
<el-row type="flex">
|
||||||
<el-col :span="16">
|
<project-change :project-name="currentProject"/>
|
||||||
|
<el-col :span="14">
|
||||||
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router
|
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router
|
||||||
:default-active='$route.path'>
|
:default-active='$route.path'>
|
||||||
<el-submenu :class="{'deactivation':!isProjectActivation}"
|
|
||||||
v-permission="['test_manager','test_user','test_viewer']" index="3" popper-class="submenu">
|
|
||||||
<template v-slot:title>
|
|
||||||
<span style="display: inline-block;width: 150px;white-space:nowrap; overflow:hidden; text-overflow:ellipsis;" :title="currentProject">
|
|
||||||
{{ $t('commons.project') }}: {{currentProject}}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<search-list ref="projectRecent" :options="projectRecent" :current-project.sync="currentProject"/>
|
|
||||||
<el-divider/>
|
|
||||||
<el-menu-item :index="'/setting/project/create'">
|
|
||||||
<font-awesome-icon :icon="['fa', 'plus']"/>
|
|
||||||
<span style="padding-left: 7px;">{{ $t("project.create") }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<ms-show-all :index="'/setting/project/all'"/>
|
|
||||||
</el-submenu>
|
|
||||||
|
|
||||||
<el-menu-item :index="'/track/home'">
|
<el-menu-item :index="'/track/home'">
|
||||||
{{ $t("i18n.home") }}
|
{{ $t("i18n.home") }}
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
@ -70,10 +55,11 @@ import MsRecentList from "../../common/head/RecentList";
|
||||||
import MsCreateButton from "../../common/head/CreateButton";
|
import MsCreateButton from "../../common/head/CreateButton";
|
||||||
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
||||||
import SearchList from "@/business/components/common/head/SearchList";
|
import SearchList from "@/business/components/common/head/SearchList";
|
||||||
|
import ProjectChange from "@/business/components/common/head/ProjectSwitch";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TrackHeaderMenus",
|
name: "TrackHeaderMenus",
|
||||||
components: {SearchList, MsShowAll, MsRecentList, MsCreateButton},
|
components: {ProjectChange, SearchList, MsShowAll, MsRecentList, MsCreateButton},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
testPlanViewPath: '',
|
testPlanViewPath: '',
|
||||||
|
@ -83,16 +69,6 @@ export default {
|
||||||
testCaseProjectPath: '',
|
testCaseProjectPath: '',
|
||||||
isProjectActivation: true,
|
isProjectActivation: true,
|
||||||
currentProject: '',
|
currentProject: '',
|
||||||
projectRecent: {
|
|
||||||
title: this.$t('project.recent'),
|
|
||||||
url: "/project/recent/5",
|
|
||||||
index: function (item) {
|
|
||||||
return '/track/case/' + item.id;
|
|
||||||
},
|
|
||||||
router: function (item) {
|
|
||||||
return {name: 'testCase', params: {projectId: item.id, projectName: item.name}}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
caseRecent: {
|
caseRecent: {
|
||||||
title: this.$t('test_track.recent_case'),
|
title: this.$t('test_track.recent_case'),
|
||||||
url: "/test/case/recent/5",
|
url: "/test/case/recent/5",
|
||||||
|
@ -140,14 +116,6 @@ export default {
|
||||||
},
|
},
|
||||||
init() {
|
init() {
|
||||||
let path = this.$route.path;
|
let path = this.$route.path;
|
||||||
// if (path.indexOf("/track/case") >= 0 && !!this.$route.params.projectId) {
|
|
||||||
// this.testCaseProjectPath = path;
|
|
||||||
// //不激活项目菜单栏
|
|
||||||
// this.isProjectActivation = false;
|
|
||||||
// this.reload();
|
|
||||||
// } else {
|
|
||||||
// this.isProjectActivation = true;
|
|
||||||
// }
|
|
||||||
if (path.indexOf("/track/plan/view") >= 0) {
|
if (path.indexOf("/track/plan/view") >= 0) {
|
||||||
this.testPlanViewPath = path;
|
this.testPlanViewPath = path;
|
||||||
this.reload();
|
this.reload();
|
||||||
|
@ -163,11 +131,7 @@ export default {
|
||||||
},
|
},
|
||||||
registerEvents() {
|
registerEvents() {
|
||||||
TrackEvent.$on(LIST_CHANGE, () => {
|
TrackEvent.$on(LIST_CHANGE, () => {
|
||||||
// todo 这里偶尔会有 refs 为空的情况
|
// // todo 这里偶尔会有 refs 为空的情况
|
||||||
if (!this.$refs.projectRecent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$refs.projectRecent.recent();
|
|
||||||
this.$refs.planRecent.recent();
|
this.$refs.planRecent.recent();
|
||||||
this.$refs.caseRecent.recent();
|
this.$refs.caseRecent.recent();
|
||||||
});
|
});
|
||||||
|
@ -199,4 +163,11 @@ export default {
|
||||||
border-bottom: white !important;
|
border-bottom: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*.project-change {*/
|
||||||
|
/* height: 40px;*/
|
||||||
|
/* line-height: 40px;*/
|
||||||
|
/* color: inherit;*/
|
||||||
|
/* margin-left: 20px;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -124,6 +124,7 @@ export default {
|
||||||
already_exists: 'The name already exists',
|
already_exists: 'The name already exists',
|
||||||
modifier: 'Modifier',
|
modifier: 'Modifier',
|
||||||
validate: "Validate",
|
validate: "Validate",
|
||||||
|
batch_add: "Batch add",
|
||||||
date: {
|
date: {
|
||||||
select_date: 'Select date',
|
select_date: 'Select date',
|
||||||
start_date: 'Start date',
|
start_date: 'Start date',
|
||||||
|
|
|
@ -124,6 +124,7 @@ export default {
|
||||||
already_exists: '名称不能重复',
|
already_exists: '名称不能重复',
|
||||||
modifier: '修改人',
|
modifier: '修改人',
|
||||||
validate: "校验",
|
validate: "校验",
|
||||||
|
batch_add: "批量添加",
|
||||||
date: {
|
date: {
|
||||||
select_date: '选择日期',
|
select_date: '选择日期',
|
||||||
start_date: '开始日期',
|
start_date: '开始日期',
|
||||||
|
@ -589,7 +590,7 @@ export default {
|
||||||
select_table: "选择可见数据",
|
select_table: "选择可见数据",
|
||||||
select_all: "选择全部数据"
|
select_all: "选择全部数据"
|
||||||
},
|
},
|
||||||
report_name_info: '请输入报名名称',
|
report_name_info: '请输入报告名称',
|
||||||
save_case_info: '请先保存用例',
|
save_case_info: '请先保存用例',
|
||||||
reference_deleted: '引用已删除',
|
reference_deleted: '引用已删除',
|
||||||
},
|
},
|
||||||
|
|
|
@ -124,6 +124,7 @@ export default {
|
||||||
already_exists: '名稱不能重復',
|
already_exists: '名稱不能重復',
|
||||||
modifier: '修改人',
|
modifier: '修改人',
|
||||||
validate: "校驗",
|
validate: "校驗",
|
||||||
|
batch_add: "批量添加",
|
||||||
date: {
|
date: {
|
||||||
select_date: '選擇日期',
|
select_date: '選擇日期',
|
||||||
start_date: '開始日期',
|
start_date: '開始日期',
|
||||||
|
@ -166,7 +167,7 @@ export default {
|
||||||
current_user: "是當前用戶"
|
current_user: "是當前用戶"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
monitor:"監控",
|
monitor: "監控",
|
||||||
all_label: {
|
all_label: {
|
||||||
case: "全部用例",
|
case: "全部用例",
|
||||||
review: "全部評審"
|
review: "全部評審"
|
||||||
|
@ -589,7 +590,7 @@ export default {
|
||||||
select_table: "選擇可見數據",
|
select_table: "選擇可見數據",
|
||||||
select_all: "選擇全部數據"
|
select_all: "選擇全部數據"
|
||||||
},
|
},
|
||||||
report_name_info: '請輸入報名名稱',
|
report_name_info: '請輸入報告名稱',
|
||||||
save_case_info: '請先保存用例',
|
save_case_info: '請先保存用例',
|
||||||
reference_deleted: '引用已删除',
|
reference_deleted: '引用已删除',
|
||||||
},
|
},
|
||||||
|
@ -829,14 +830,14 @@ export default {
|
||||||
not_exist: "測試報告不存在",
|
not_exist: "測試報告不存在",
|
||||||
},
|
},
|
||||||
api_monitor: {
|
api_monitor: {
|
||||||
to:"到",
|
to: "到",
|
||||||
start_time:"開始時間",
|
start_time: "開始時間",
|
||||||
end_time:"結束時間",
|
end_time: "結束時間",
|
||||||
today:"今天",
|
today: "今天",
|
||||||
this_week:"本週",
|
this_week: "本週",
|
||||||
this_mouth:"本月",
|
this_mouth: "本月",
|
||||||
please_search:"請搜索",
|
please_search: "請搜索",
|
||||||
date:"日期"
|
date: "日期"
|
||||||
},
|
},
|
||||||
test_track: {
|
test_track: {
|
||||||
test_track: "測試跟蹤",
|
test_track: "測試跟蹤",
|
||||||
|
|
Loading…
Reference in New Issue