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.ReferenceDTO;
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
|
||||
import io.metersphere.api.service.ApiDefinitionService;
|
||||
import io.metersphere.base.domain.ApiDefinition;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
|
@ -93,7 +94,7 @@ public class ApiDefinitionController {
|
|||
|
||||
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
|
||||
@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);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,5 +13,7 @@ public class ApiTestImportRequest {
|
|||
private String projectId;
|
||||
private String platform;
|
||||
private Boolean useEnvironment;
|
||||
// 来自场景的导入不需要存储
|
||||
private boolean saved = true;
|
||||
private String swaggerUrl;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public class MsParser extends ApiImportAbstractParser {
|
|||
if (testObject.get("projectName") != null) {
|
||||
return parseMsFormat(testStr, request);
|
||||
} else {
|
||||
return parsePluginFormat(testObject);
|
||||
return parsePluginFormat(testObject, request.isSaved());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,14 +53,16 @@ public class MsParser extends ApiImportAbstractParser {
|
|||
return apiDefinitionImport;
|
||||
}
|
||||
|
||||
private ApiDefinitionImport parsePluginFormat(JSONObject testObject) {
|
||||
private ApiDefinitionImport parsePluginFormat(JSONObject testObject, boolean isSaved) {
|
||||
List<ApiDefinitionResult> results = new ArrayList<>();
|
||||
ApiDefinitionImport apiImport = new ApiDefinitionImport();
|
||||
apiImport.setProtocol(RequestType.HTTP);
|
||||
apiImport.setData(results);
|
||||
testObject.keySet().forEach(tag -> {
|
||||
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
|
||||
createModule(module);
|
||||
if (isSaved) {
|
||||
createModule(module);
|
||||
}
|
||||
JSONObject requests = testObject.getJSONObject(tag);
|
||||
requests.keySet().forEach(requestName -> {
|
||||
|
||||
|
|
|
@ -30,17 +30,17 @@ public class PostmanParser extends ApiImportAbstractParser {
|
|||
List<PostmanKeyValue> variables = postmanCollection.getVariable();
|
||||
ApiDefinitionImport apiImport = new ApiDefinitionImport();
|
||||
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);
|
||||
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) {
|
||||
List<PostmanItem> childItems = item.getItem();
|
||||
if (childItems != null) {
|
||||
ApiModule module = buildModule(item.getName(), parentModule);
|
||||
parseItem(childItems, variables, results, module);
|
||||
ApiModule module = buildModule(item.getName(), parentModule, isSaved);
|
||||
parseItem(childItems, variables, results, module, isSaved);
|
||||
} else {
|
||||
ApiDefinitionResult request = parsePostman(item);
|
||||
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);
|
||||
ApiModule module;
|
||||
if (parentModule != null) {
|
||||
|
@ -62,7 +62,9 @@ public class PostmanParser extends ApiImportAbstractParser {
|
|||
} else {
|
||||
module = apiModuleService.getNewModule(name, this.projectId, 1);
|
||||
}
|
||||
createModule(module);
|
||||
if (isSaved) {
|
||||
createModule(module);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,11 +39,11 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
}
|
||||
ApiDefinitionImport definitionImport = new ApiDefinitionImport();
|
||||
this.projectId = request.getProjectId();
|
||||
definitionImport.setData(parseRequests(swagger));
|
||||
definitionImport.setData(parseRequests(swagger, request.isSaved()));
|
||||
return definitionImport;
|
||||
}
|
||||
|
||||
private List<ApiDefinitionResult> parseRequests(Swagger swagger) {
|
||||
private List<ApiDefinitionResult> parseRequests(Swagger swagger, boolean isSaved) {
|
||||
Map<String, Path> paths = swagger.getPaths();
|
||||
Set<String> pathNames = paths.keySet();
|
||||
|
||||
|
@ -62,7 +62,7 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
parseParameters(operation, request);
|
||||
apiDefinition.setRequest(JSON.toJSONString(request));
|
||||
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses())));
|
||||
buildModule(apiDefinition, operation);
|
||||
buildModule(apiDefinition, operation, isSaved);
|
||||
results.add(apiDefinition);
|
||||
}
|
||||
}
|
||||
|
@ -71,13 +71,15 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
return results;
|
||||
}
|
||||
|
||||
private void buildModule(ApiDefinitionResult apiDefinition, Operation operation) {
|
||||
private void buildModule(ApiDefinitionResult apiDefinition, Operation operation, boolean isSaved) {
|
||||
List<String> tags = operation.getTags();
|
||||
if (tags != null) {
|
||||
tags.forEach(tag -> {
|
||||
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
|
||||
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
|
||||
createModule(module);
|
||||
if (isSaved) {
|
||||
createModule(module);
|
||||
}
|
||||
apiDefinition.setModuleId(module.getId());
|
||||
});
|
||||
}
|
||||
|
@ -113,8 +115,8 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
for (Parameter parameter : parameters) {
|
||||
switch (parameter.getIn()) {
|
||||
case SwaggerParameterType.PATH:
|
||||
parsePathParameters(parameter, request.getRest());
|
||||
break;
|
||||
parsePathParameters(parameter, request.getRest());
|
||||
break;
|
||||
case SwaggerParameterType.QUERY:
|
||||
parseQueryParameters(parameter, request.getArguments());
|
||||
break;
|
||||
|
@ -297,14 +299,14 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
|| value instanceof DecimalProperty) {
|
||||
return 0.0;
|
||||
} else {// todo 其他类型?
|
||||
return getDefaultStringValue(value.getDescription());
|
||||
return getDefaultStringValue(value.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFormDataParameters(FormParameter parameter, Body body) {
|
||||
List<KeyValue> keyValues = Optional.ofNullable(body.getKvs()).orElse(new ArrayList<>());
|
||||
KeyValue kv = new KeyValue(parameter.getName(), "", getDefaultStringValue(parameter.getDescription()));
|
||||
if (StringUtils.equals(parameter.getType(), "file") ) {
|
||||
if (StringUtils.equals(parameter.getType(), "file")) {
|
||||
kv.setType("file");
|
||||
}
|
||||
keyValues.add(kv);
|
||||
|
|
|
@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON;
|
|||
import io.metersphere.api.jmeter.TestResult;
|
||||
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
||||
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -18,14 +17,9 @@ import java.util.UUID;
|
|||
public class ApiDefinitionExecResultService {
|
||||
@Resource
|
||||
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
|
||||
@Resource
|
||||
private ExtApiDefinitionExecResultMapper extApiDefinitionExecResultMapper;
|
||||
|
||||
|
||||
public void saveApiResult(TestResult result) {
|
||||
result.getScenarios().get(0).getRequestResults().forEach(item -> {
|
||||
// 清理原始资源,每个执行 保留一条结果
|
||||
extApiDefinitionExecResultMapper.deleteByResourceId(item.getName());
|
||||
ApiDefinitionExecResult saveResult = new ApiDefinitionExecResult();
|
||||
saveResult.setId(UUID.randomUUID().toString());
|
||||
saveResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
|
||||
|
|
|
@ -326,7 +326,7 @@ public class ApiDefinitionService {
|
|||
* @return
|
||||
*/
|
||||
public APIReportResult getDbResult(String testId) {
|
||||
ApiDefinitionExecResult result = extApiDefinitionExecResultMapper.selectByResourceId(testId);
|
||||
ApiDefinitionExecResult result = extApiDefinitionExecResultMapper.selectMaxResultByResourceId(testId);
|
||||
if (result == 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());
|
||||
ApiDefinitionImport apiImport = null;
|
||||
try {
|
||||
|
@ -345,8 +345,10 @@ public class ApiDefinitionService {
|
|||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(Translator.get("parse_data_error"));
|
||||
}
|
||||
importApiTest(request, apiImport);
|
||||
return "SUCCESS";
|
||||
if (request.isSaved()) {
|
||||
importApiTest(request, apiImport);
|
||||
}
|
||||
return apiImport;
|
||||
}
|
||||
|
||||
private void importApiTest(ApiTestImportRequest importRequest, ApiDefinitionImport apiImport) {
|
||||
|
|
|
@ -6,7 +6,6 @@ public interface ExtApiDefinitionExecResultMapper {
|
|||
|
||||
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>
|
||||
|
||||
<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
|
||||
where resource_id = #{resourceId,jdbcType=VARCHAR}
|
||||
where resource_id = #{resourceId,jdbcType=VARCHAR} ORDER BY update_time DESC LIMIT 1
|
||||
</select>
|
||||
</mapper>
|
|
@ -89,17 +89,29 @@
|
|||
|
||||
<select id="selectByIds" resultType="io.metersphere.api.dto.definition.ApiComputeResult">
|
||||
select t1.api_definition_id apiDefinitionId,count(t1.id) caseTotal,
|
||||
case t2.status
|
||||
when 'success' then '通过'
|
||||
when 'error' then '未通过'
|
||||
ELSE '未执行' end as status ,
|
||||
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
|
||||
case t2.status
|
||||
when 'success' then '通过'
|
||||
when 'error' then '未通过'
|
||||
ELSE '未执行' end as status ,
|
||||
CONCAT(FORMAT(SUM(IF(t2.`status` = 'success', 1, 0))/ COUNT(t1.id)*100, 2), '%') passRate
|
||||
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
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
#{v}
|
||||
</foreach>
|
||||
order by t2.end_time desc;
|
||||
</select>
|
||||
|
||||
<sql id="combine">
|
||||
|
|
|
@ -146,15 +146,45 @@
|
|||
</sql>
|
||||
|
||||
<select id="list" resultType="io.metersphere.api.dto.definition.ApiTestCaseResult">
|
||||
select atc.id, atc.project_id,
|
||||
atc.name,atc.priority,atc.api_definition_id,T1.name as createUser ,T2.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 T1 on atc.create_user_id = T1.id left join user T2 on
|
||||
atc.update_user_id = T2.id left join api_definition_exec_result ader on atc.id = ader.resource_id
|
||||
select
|
||||
atc.id,
|
||||
atc.project_id,
|
||||
atc.name,
|
||||
atc.priority,
|
||||
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>
|
||||
<if test="request.name != null and request.name!=''">
|
||||
|
||||
and atc.name like CONCAT('%', #{request.name},'%')
|
||||
</if>
|
||||
<if test="request.id != null and request.id!=''">
|
||||
|
|
|
@ -84,9 +84,4 @@ public class ProjectController {
|
|||
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-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-col>
|
||||
<el-col :span="3">
|
||||
{{request.responseResult.responseTime}} ms
|
||||
<span v-if="request.success">
|
||||
{{request.responseResult.responseTime}} ms
|
||||
</span>
|
||||
<span style="color: #FE6F71" v-else>
|
||||
{{request.responseResult.responseTime}} ms
|
||||
</span>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="2">
|
||||
<div class="success">
|
||||
<div>
|
||||
<el-tag size="mini" type="success" v-if="request.success">
|
||||
{{ $t('api_report.success') }}
|
||||
</el-tag>
|
||||
|
|
|
@ -8,33 +8,10 @@
|
|||
</div>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<div class="name">{{request.name}}</div>
|
||||
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
|
||||
<div class="url">{{request.url}}</div>
|
||||
</el-tooltip>
|
||||
</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>
|
||||
</div>
|
||||
<el-collapse-transition>
|
||||
|
@ -52,7 +29,6 @@
|
|||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div v-else>
|
||||
<ms-request-metric :request="request"/>
|
||||
<ms-request-text v-if="isCodeEditAlive" :request="request"/>
|
||||
<br>
|
||||
<ms-response-text :request-type="requestType" v-if="isCodeEditAlive" :response="request.responseResult"/>
|
||||
|
|
|
@ -32,36 +32,40 @@
|
|||
methods: {
|
||||
setFiles(item, bodyUploadFiles, obj) {
|
||||
if (item.body) {
|
||||
item.body.kvs.forEach(param => {
|
||||
if (param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
if (item.body.kvs) {
|
||||
item.body.kvs.forEach(param => {
|
||||
if (param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
}
|
||||
obj.bodyUploadIds.push(item.id);
|
||||
bodyUploadFiles.push(item.file);
|
||||
}
|
||||
obj.bodyUploadIds.push(item.id);
|
||||
bodyUploadFiles.push(item.file);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
item.body.binary.forEach(param => {
|
||||
if (param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (item.body.binary) {
|
||||
item.body.binary.forEach(param => {
|
||||
if (param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
}
|
||||
obj.bodyUploadIds.push(item.id);
|
||||
bodyUploadFiles.push(item.file);
|
||||
}
|
||||
obj.bodyUploadIds.push(item.id);
|
||||
bodyUploadFiles.push(item.file);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
recursiveFile(arr, bodyUploadFiles, obj) {
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
</el-form>
|
||||
|
||||
<!-- 场景步骤-->
|
||||
<div v-loading="isReloadData">
|
||||
<div v-loading="loading">
|
||||
<p class="tip">{{$t('api_test.automation.scenario_step')}} </p>
|
||||
<el-row>
|
||||
<el-col :span="21">
|
||||
|
@ -157,13 +157,13 @@
|
|||
</el-row>
|
||||
</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"
|
||||
:default-expanded-keys="expandedNode"
|
||||
:expand-on-click-node="false"
|
||||
@node-expand="nodeExpand"
|
||||
@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%">
|
||||
<template>
|
||||
<!-- 场景 -->
|
||||
|
@ -267,6 +267,8 @@
|
|||
|
||||
<!--场景公共参数-->
|
||||
<ms-scenario-parameters :currentScenario="currentScenario" @addParameters="addParameters" ref="scenarioParameters"/>
|
||||
<!--外部导入-->
|
||||
<api-import ref="apiImport" :saved="false" @refresh="apiImport"/>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
@ -293,6 +295,7 @@
|
|||
import MsApiScenarioComponent from "./ApiScenarioComponent";
|
||||
import MsApiReportDetail from "../report/ApiReportDetail";
|
||||
import MsScenarioParameters from "./ScenarioParameters";
|
||||
import ApiImport from "../../definition/components/import/ApiImport";
|
||||
|
||||
export default {
|
||||
name: "EditApiScenario",
|
||||
|
@ -301,13 +304,21 @@
|
|||
currentScenario: {},
|
||||
},
|
||||
components: {
|
||||
ApiEnvironmentConfig, MsScenarioParameters,
|
||||
MsApiReportDetail, MsAddTag, MsRun,
|
||||
MsApiScenarioComponent, MsImportApiScenario,
|
||||
MsJsr233Processor, MsConstantTimer,
|
||||
MsIfController, MsApiAssertions,
|
||||
MsApiExtract, MsApiDefinition,
|
||||
MsApiComponent, MsApiCustomize
|
||||
ApiEnvironmentConfig,
|
||||
MsScenarioParameters,
|
||||
MsApiReportDetail,
|
||||
MsAddTag, MsRun,
|
||||
MsApiScenarioComponent,
|
||||
MsImportApiScenario,
|
||||
MsJsr233Processor,
|
||||
MsConstantTimer,
|
||||
MsIfController,
|
||||
MsApiAssertions,
|
||||
MsApiExtract,
|
||||
MsApiDefinition,
|
||||
MsApiComponent,
|
||||
MsApiCustomize,
|
||||
ApiImport,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -333,7 +344,7 @@
|
|||
options: API_STATUS,
|
||||
levels: PRIORITY,
|
||||
scenario: {},
|
||||
isReloadData: false,
|
||||
loading: false,
|
||||
apiListVisible: false,
|
||||
customizeVisible: false,
|
||||
scenarioVisible: false,
|
||||
|
@ -402,6 +413,7 @@
|
|||
this.scenarioVisible = true;
|
||||
break;
|
||||
default:
|
||||
this.$refs.apiImport.open();
|
||||
break;
|
||||
}
|
||||
this.sort();
|
||||
|
@ -553,9 +565,9 @@
|
|||
this.reload();
|
||||
},
|
||||
reload() {
|
||||
this.isReloadData = true
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.isReloadData = false
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
runDebug() {
|
||||
|
@ -622,36 +634,40 @@
|
|||
},
|
||||
setFiles(item, bodyUploadFiles, obj) {
|
||||
if (item.body) {
|
||||
item.body.kvs.forEach(param => {
|
||||
if (param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
if (item.body.kvs) {
|
||||
item.body.kvs.forEach(param => {
|
||||
if (param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
}
|
||||
obj.bodyUploadIds.push(item.id);
|
||||
bodyUploadFiles.push(item.file);
|
||||
}
|
||||
obj.bodyUploadIds.push(item.id);
|
||||
bodyUploadFiles.push(item.file);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
item.body.binary.forEach(param => {
|
||||
if (param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (item.body.binary) {
|
||||
item.body.binary.forEach(param => {
|
||||
if (param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
}
|
||||
obj.bodyUploadIds.push(item.id);
|
||||
bodyUploadFiles.push(item.file);
|
||||
}
|
||||
obj.bodyUploadIds.push(item.id);
|
||||
bodyUploadFiles.push(item.file);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
recursiveFile(arr, bodyUploadFiles, obj) {
|
||||
|
@ -728,7 +744,7 @@
|
|||
},
|
||||
runRefresh() {
|
||||
this.debugVisible = true;
|
||||
this.isReloadData = false;
|
||||
this.loading = false;
|
||||
},
|
||||
showScenarioParameters() {
|
||||
this.$refs.scenarioParameters.open(this.currentScenario.variables);
|
||||
|
@ -736,6 +752,15 @@
|
|||
addParameters(data) {
|
||||
this.currentScenario.variables = data;
|
||||
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) {
|
||||
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));
|
||||
},
|
||||
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') }}
|
||||
</el-radio>
|
||||
</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"
|
||||
:parameters="body.kvs"
|
||||
:isShowEnable="isShowEnable"
|
||||
|
@ -55,12 +59,14 @@
|
|||
type="body"
|
||||
v-if="body.type == 'BINARY'"/>
|
||||
|
||||
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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 MsJsonCodeEdit from "../../../../common/components/MsJsonCodeEdit";
|
||||
|
||||
|
@ -68,6 +74,7 @@
|
|||
import MsApiVariable from "../ApiVariable";
|
||||
import MsApiBinaryVariable from "./ApiBinaryVariable";
|
||||
import MsApiFromUrlVariable from "./ApiFromUrlVariable";
|
||||
import BatchAddParameter from "../basis/BatchAddParameter";
|
||||
|
||||
export default {
|
||||
name: "MsApiBody",
|
||||
|
@ -78,7 +85,8 @@
|
|||
MsApiKeyValue,
|
||||
MsApiBinaryVariable,
|
||||
MsApiFromUrlVariable,
|
||||
MsJsonCodeEdit
|
||||
MsJsonCodeEdit,
|
||||
BatchAddParameter
|
||||
},
|
||||
props: {
|
||||
body: {},
|
||||
|
@ -147,9 +155,29 @@
|
|||
},
|
||||
jsonError(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() {
|
||||
if (!this.body.type) {
|
||||
this.body.type = BODY_TYPE.FORM_DATA;
|
||||
|
@ -187,4 +215,8 @@
|
|||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.ms-el-link {
|
||||
float: right;
|
||||
margin-right: 45px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -70,6 +70,12 @@
|
|||
export default {
|
||||
name: "ApiImport",
|
||||
components: {MsDialogFooter},
|
||||
props: {
|
||||
saved: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
|
@ -148,7 +154,7 @@
|
|||
},
|
||||
uploadValidate(file, fileList) {
|
||||
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'));
|
||||
return false;
|
||||
}
|
||||
|
@ -170,7 +176,7 @@
|
|||
let res = response.data;
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.visible = false;
|
||||
this.$emit('refresh');
|
||||
this.$emit('refresh', res);
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
|
@ -181,8 +187,11 @@
|
|||
let param = {};
|
||||
Object.assign(param, this.formData);
|
||||
param.platform = this.selectedPlatformValue;
|
||||
param.moduleId = this.currentModule.id;
|
||||
param.modulePath = this.currentModule.path;
|
||||
param.saved = this.saved;
|
||||
if (this.currentModule) {
|
||||
param.moduleId = this.currentModule.id;
|
||||
param.modulePath = this.currentModule.path;
|
||||
}
|
||||
param.projectId = getCurrentProjectID();
|
||||
if (!this.swaggerUrlEable) {
|
||||
param.swaggerUrl = undefined;
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
</div>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers"/>
|
||||
</el-tab-pane>
|
||||
|
||||
|
@ -25,7 +24,9 @@
|
|||
<div class="el-step__icon-inner">{{request.arguments.length-1}}</div>
|
||||
</div></span>
|
||||
</el-tooltip>
|
||||
|
||||
<el-row>
|
||||
<el-link class="ms-el-link" @click="batchAdd"> {{$t("commons.batch_add")}}</el-link>
|
||||
</el-row>
|
||||
<ms-api-variable :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.arguments"/>
|
||||
</el-tab-pane>
|
||||
|
||||
|
@ -39,6 +40,9 @@
|
|||
</div>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<el-row>
|
||||
<el-link class="ms-el-link" @click="batchAdd"> {{$t("commons.batch_add")}}</el-link>
|
||||
</el-row>
|
||||
<ms-api-variable :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.rest"/>
|
||||
</el-tab-pane>
|
||||
|
||||
|
@ -71,6 +75,9 @@
|
|||
<!--提取规则-->
|
||||
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
|
||||
</div>
|
||||
|
||||
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
|
||||
|
||||
</div>
|
||||
</el-col>
|
||||
<!--操作按钮-->
|
||||
|
@ -98,15 +105,24 @@
|
|||
import {createComponent} from "../../jmeter/components";
|
||||
import MsApiAssertions from "../../assertion/ApiAssertions";
|
||||
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 BatchAddParameter from "../../basis/BatchAddParameter";
|
||||
|
||||
|
||||
export default {
|
||||
name: "MsApiHttpRequestForm",
|
||||
components: {
|
||||
MsJsr233Processor,
|
||||
MsApiAdvancedConfig,
|
||||
MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAuthConfig, MsApiBody, MsApiKeyValue, MsApiAssertions
|
||||
BatchAddParameter,
|
||||
MsApiVariable,
|
||||
ApiRequestMethodSelect,
|
||||
MsApiExtract,
|
||||
MsApiAuthConfig,
|
||||
MsApiBody,
|
||||
MsApiKeyValue,
|
||||
MsApiAssertions
|
||||
},
|
||||
props: {
|
||||
request: {},
|
||||
|
@ -149,7 +165,7 @@
|
|||
},
|
||||
headerSuggestions: REQUEST_HEADERS,
|
||||
isReloadData: false,
|
||||
isBodyShow: true
|
||||
isBodyShow: true,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -216,6 +232,35 @@
|
|||
this.$nextTick(() => {
|
||||
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;
|
||||
}
|
||||
|
||||
.ms-el-link {
|
||||
float: right;
|
||||
margin-right: 45px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,25 +1,10 @@
|
|||
<template>
|
||||
<div id="menu-bar" v-if="isRouterAlive">
|
||||
<el-row type="flex">
|
||||
<project-change :project-name="currentProject"/>
|
||||
<el-col :span="14">
|
||||
<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'">
|
||||
{{ $t("i18n.home") }}
|
||||
</el-menu-item>
|
||||
|
@ -75,22 +60,13 @@ import MsCreateButton from "../../common/head/CreateButton";
|
|||
import MsCreateTest from "../../common/head/CreateTest";
|
||||
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
|
||||
import SearchList from "@/business/components/common/head/SearchList";
|
||||
import ProjectChange from "@/business/components/common/head/ProjectSwitch";
|
||||
|
||||
export default {
|
||||
name: "MsApiHeaderMenus",
|
||||
components: {SearchList, MsCreateTest, MsCreateButton, MsShowAll, MsRecentList},
|
||||
components: {SearchList, MsCreateTest, MsCreateButton, MsShowAll, MsRecentList, ProjectChange},
|
||||
data() {
|
||||
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: {
|
||||
title: this.$t('load_test.recent'),
|
||||
url: "/api/recent/5",
|
||||
|
@ -115,19 +91,10 @@ export default {
|
|||
currentProject: ''
|
||||
}
|
||||
},
|
||||
// watch: {
|
||||
// '$route'(to) {
|
||||
// this.init();
|
||||
// },
|
||||
// },
|
||||
methods: {
|
||||
registerEvents() {
|
||||
ApiEvent.$on(LIST_CHANGE, () => {
|
||||
// todo 这里偶尔会有 refs 为空的情况
|
||||
if (!this.$refs.projectRecent) {
|
||||
return;
|
||||
}
|
||||
this.$refs.projectRecent.recent();
|
||||
// // todo 这里偶尔会有 refs 为空的情况
|
||||
this.$refs.testRecent.recent();
|
||||
this.$refs.reportRecent.recent();
|
||||
});
|
||||
|
@ -138,17 +105,6 @@ export default {
|
|||
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() {
|
||||
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>
|
||||
<div v-loading="result.loading" class="search-list">
|
||||
<div v-loading="result.loading">
|
||||
<el-input placeholder="搜索项目"
|
||||
prefix-icon="el-icon-search"
|
||||
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) {
|
||||
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}, () => {
|
||||
localStorage.setItem(PROJECT_ID, projectId);
|
||||
if (this.$route.path.indexOf('/track/review/view/') >= 0) {
|
||||
this.$router.replace('/track/review/all');
|
||||
} else if (this.$route.path.indexOf('/track/plan/view/') >= 0) {
|
||||
this.$router.replace('/track/plan/all');
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
let path = this.$route.matched[0].path ? this.$route.matched[0].path : '/';
|
||||
this.$router.push(path).then(() => {
|
||||
window.location.reload()
|
||||
}).catch(err => err);
|
||||
this.changeProjectName(projectId);
|
||||
});
|
||||
},
|
||||
|
@ -135,7 +125,7 @@ export default {
|
|||
|
||||
.title {
|
||||
display: inline-block;
|
||||
padding-left: 20px;
|
||||
padding-left: 15px;
|
||||
max-width: 200px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -1,25 +1,9 @@
|
|||
<template>
|
||||
<div id="menu-bar">
|
||||
<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-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'">
|
||||
{{ $t("i18n.home") }}
|
||||
</el-menu-item>
|
||||
|
@ -61,10 +45,12 @@ import MsCreateButton from "../../common/head/CreateButton";
|
|||
import MsShowAll from "../../common/head/ShowAll";
|
||||
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
|
||||
import SearchList from "@/business/components/common/head/SearchList";
|
||||
import ProjectChange from "@/business/components/common/head/ProjectSwitch";
|
||||
|
||||
export default {
|
||||
name: "PerformanceHeaderMenus",
|
||||
components: {
|
||||
ProjectChange,
|
||||
SearchList,
|
||||
MsCreateButton,
|
||||
MsShowAll,
|
||||
|
@ -73,16 +59,6 @@ export default {
|
|||
},
|
||||
data() {
|
||||
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: {
|
||||
title: this.$t('load_test.recent'),
|
||||
url: "/performance/recent/5",
|
||||
|
@ -108,11 +84,7 @@ export default {
|
|||
methods: {
|
||||
registerEvents() {
|
||||
PerformanceEvent.$on(LIST_CHANGE, () => {
|
||||
// todo 这里偶尔会有 refs 为空的情况
|
||||
if (!this.$refs.projectRecent) {
|
||||
return;
|
||||
}
|
||||
this.$refs.projectRecent.recent();
|
||||
// // todo 这里偶尔会有 refs 为空的情况
|
||||
this.$refs.testRecent.recent();
|
||||
this.$refs.reportRecent.recent();
|
||||
});
|
||||
|
|
|
@ -2,25 +2,10 @@
|
|||
|
||||
<div id="menu-bar" v-if="isRouterAlive">
|
||||
<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
|
||||
: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'">
|
||||
{{ $t("i18n.home") }}
|
||||
</el-menu-item>
|
||||
|
@ -70,10 +55,11 @@ import MsRecentList from "../../common/head/RecentList";
|
|||
import MsCreateButton from "../../common/head/CreateButton";
|
||||
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
||||
import SearchList from "@/business/components/common/head/SearchList";
|
||||
import ProjectChange from "@/business/components/common/head/ProjectSwitch";
|
||||
|
||||
export default {
|
||||
name: "TrackHeaderMenus",
|
||||
components: {SearchList, MsShowAll, MsRecentList, MsCreateButton},
|
||||
components: {ProjectChange, SearchList, MsShowAll, MsRecentList, MsCreateButton},
|
||||
data() {
|
||||
return {
|
||||
testPlanViewPath: '',
|
||||
|
@ -83,16 +69,6 @@ export default {
|
|||
testCaseProjectPath: '',
|
||||
isProjectActivation: true,
|
||||
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: {
|
||||
title: this.$t('test_track.recent_case'),
|
||||
url: "/test/case/recent/5",
|
||||
|
@ -140,14 +116,6 @@ export default {
|
|||
},
|
||||
init() {
|
||||
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) {
|
||||
this.testPlanViewPath = path;
|
||||
this.reload();
|
||||
|
@ -163,11 +131,7 @@ export default {
|
|||
},
|
||||
registerEvents() {
|
||||
TrackEvent.$on(LIST_CHANGE, () => {
|
||||
// todo 这里偶尔会有 refs 为空的情况
|
||||
if (!this.$refs.projectRecent) {
|
||||
return;
|
||||
}
|
||||
this.$refs.projectRecent.recent();
|
||||
// // todo 这里偶尔会有 refs 为空的情况
|
||||
this.$refs.planRecent.recent();
|
||||
this.$refs.caseRecent.recent();
|
||||
});
|
||||
|
@ -199,4 +163,11 @@ export default {
|
|||
border-bottom: white !important;
|
||||
}
|
||||
|
||||
/*.project-change {*/
|
||||
/* height: 40px;*/
|
||||
/* line-height: 40px;*/
|
||||
/* color: inherit;*/
|
||||
/* margin-left: 20px;*/
|
||||
/*}*/
|
||||
|
||||
</style>
|
||||
|
|
|
@ -124,6 +124,7 @@ export default {
|
|||
already_exists: 'The name already exists',
|
||||
modifier: 'Modifier',
|
||||
validate: "Validate",
|
||||
batch_add: "Batch add",
|
||||
date: {
|
||||
select_date: 'Select date',
|
||||
start_date: 'Start date',
|
||||
|
|
|
@ -124,6 +124,7 @@ export default {
|
|||
already_exists: '名称不能重复',
|
||||
modifier: '修改人',
|
||||
validate: "校验",
|
||||
batch_add: "批量添加",
|
||||
date: {
|
||||
select_date: '选择日期',
|
||||
start_date: '开始日期',
|
||||
|
@ -589,7 +590,7 @@ export default {
|
|||
select_table: "选择可见数据",
|
||||
select_all: "选择全部数据"
|
||||
},
|
||||
report_name_info: '请输入报名名称',
|
||||
report_name_info: '请输入报告名称',
|
||||
save_case_info: '请先保存用例',
|
||||
reference_deleted: '引用已删除',
|
||||
},
|
||||
|
|
|
@ -124,6 +124,7 @@ export default {
|
|||
already_exists: '名稱不能重復',
|
||||
modifier: '修改人',
|
||||
validate: "校驗",
|
||||
batch_add: "批量添加",
|
||||
date: {
|
||||
select_date: '選擇日期',
|
||||
start_date: '開始日期',
|
||||
|
@ -166,7 +167,7 @@ export default {
|
|||
current_user: "是當前用戶"
|
||||
}
|
||||
},
|
||||
monitor:"監控",
|
||||
monitor: "監控",
|
||||
all_label: {
|
||||
case: "全部用例",
|
||||
review: "全部評審"
|
||||
|
@ -589,7 +590,7 @@ export default {
|
|||
select_table: "選擇可見數據",
|
||||
select_all: "選擇全部數據"
|
||||
},
|
||||
report_name_info: '請輸入報名名稱',
|
||||
report_name_info: '請輸入報告名稱',
|
||||
save_case_info: '請先保存用例',
|
||||
reference_deleted: '引用已删除',
|
||||
},
|
||||
|
@ -829,14 +830,14 @@ export default {
|
|||
not_exist: "測試報告不存在",
|
||||
},
|
||||
api_monitor: {
|
||||
to:"到",
|
||||
start_time:"開始時間",
|
||||
end_time:"結束時間",
|
||||
today:"今天",
|
||||
this_week:"本週",
|
||||
this_mouth:"本月",
|
||||
please_search:"請搜索",
|
||||
date:"日期"
|
||||
to: "到",
|
||||
start_time: "開始時間",
|
||||
end_time: "結束時間",
|
||||
today: "今天",
|
||||
this_week: "本週",
|
||||
this_mouth: "本月",
|
||||
please_search: "請搜索",
|
||||
date: "日期"
|
||||
},
|
||||
test_track: {
|
||||
test_track: "測試跟蹤",
|
||||
|
|
Loading…
Reference in New Issue