Merge branch 'v1.0' of github.com:metersphere/metersphere into v1.0
This commit is contained in:
commit
6a8892d166
|
@ -9,7 +9,6 @@ import io.metersphere.api.dto.SaveAPITestRequest;
|
|||
import io.metersphere.api.service.APITestService;
|
||||
import io.metersphere.base.domain.ApiTest;
|
||||
import io.metersphere.base.domain.ApiTestWithBLOBs;
|
||||
import io.metersphere.base.domain.LoadTest;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
|
@ -19,10 +18,10 @@ import org.apache.shiro.authz.annotation.RequiresRoles;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/api")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package io.metersphere.api.dto;
|
||||
|
||||
import io.metersphere.api.dto.scenario.Scenario;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class SaveAPITestRequest {
|
||||
|
@ -13,5 +16,5 @@ public class SaveAPITestRequest {
|
|||
|
||||
private String name;
|
||||
|
||||
private String scenarioDefinition;
|
||||
private List<Scenario> scenarioDefinition;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Body {
|
||||
private String type;
|
||||
private String raw;
|
||||
private List<KeyValue> kvs;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class KeyValue {
|
||||
private String name;
|
||||
private String value;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.metersphere.api.dto.scenario.assertions.Assertions;
|
||||
import io.metersphere.api.dto.scenario.extract.Extract;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Request {
|
||||
private String name;
|
||||
private String url;
|
||||
private String method;
|
||||
private List<KeyValue> parameters;
|
||||
private List<KeyValue> headers;
|
||||
private Body body;
|
||||
private Assertions assertions;
|
||||
private Extract extract;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Scenario {
|
||||
private String name;
|
||||
private String url;
|
||||
private List<KeyValue> variables;
|
||||
private List<KeyValue> headers;
|
||||
private List<Request> requests;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.api.dto.scenario.assertions;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AssertionDuration extends AssertionType {
|
||||
private long value;
|
||||
|
||||
public AssertionDuration() {
|
||||
setType(AssertionType.DURATION);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.api.dto.scenario.assertions;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AssertionRegex extends AssertionType {
|
||||
private String subject;
|
||||
private String expression;
|
||||
private String description;
|
||||
|
||||
public AssertionRegex() {
|
||||
setType(AssertionType.REGEX);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.assertions;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AssertionType {
|
||||
public final static String REGEX = "Regex";
|
||||
public final static String DURATION = "Duration";
|
||||
public final static String TEXT = "Text";
|
||||
|
||||
private String type;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.api.dto.scenario.assertions;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Assertions {
|
||||
private List<AssertionRegex> regex;
|
||||
private AssertionDuration duration;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Extract {
|
||||
private List<ExtractRegex> regex;
|
||||
private List<ExtractJSONPath> json;
|
||||
private List<ExtractXPath> xpath;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ExtractCommon extends ExtractType {
|
||||
private String variable;
|
||||
private String value; // value: ${variable}
|
||||
private String expression;
|
||||
private String description;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ExtractJSONPath extends ExtractCommon {
|
||||
public ExtractJSONPath() {
|
||||
setType(ExtractType.JSON_PATH);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ExtractRegex extends ExtractCommon {
|
||||
public ExtractRegex() {
|
||||
setType(ExtractType.REGEX);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ExtractType {
|
||||
public final static String REGEX = "Regex";
|
||||
public final static String JSON_PATH = "JSONPath";
|
||||
public final static String XPATH = "XPath";
|
||||
|
||||
private String type;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ExtractXPath extends ExtractCommon {
|
||||
public ExtractXPath() {
|
||||
setType(ExtractType.XPATH);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.APITestResult;
|
||||
import io.metersphere.api.dto.QueryAPITestRequest;
|
||||
import io.metersphere.api.dto.SaveAPITestRequest;
|
||||
|
@ -25,6 +26,7 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -73,10 +75,17 @@ public class APITestService {
|
|||
}
|
||||
|
||||
public void copy(SaveAPITestRequest request) {
|
||||
request.setName(request.getName() + " Copy");
|
||||
try {
|
||||
checkNameExist(request);
|
||||
} catch (Exception e) {
|
||||
request.setName(request.getName() + " " + new Random().nextInt(1000));
|
||||
}
|
||||
|
||||
// copy test
|
||||
ApiTestWithBLOBs copy = get(request.getId());
|
||||
copy.setId(UUID.randomUUID().toString());
|
||||
copy.setName(copy.getName() + " Copy");
|
||||
copy.setName(request.getName());
|
||||
copy.setCreateTime(System.currentTimeMillis());
|
||||
copy.setUpdateTime(System.currentTimeMillis());
|
||||
copy.setStatus(APITestStatus.Saved.name());
|
||||
|
@ -96,6 +105,10 @@ public class APITestService {
|
|||
return apiTestMapper.selectByPrimaryKey(id);
|
||||
}
|
||||
|
||||
public List<ApiTest> getApiTestByProjectId(String projectId) {
|
||||
return extApiTestMapper.getApiTestByProjectId(projectId);
|
||||
}
|
||||
|
||||
public void delete(String testId) {
|
||||
deleteFileByTestId(testId);
|
||||
apiReportService.deleteByTestId(testId);
|
||||
|
@ -138,7 +151,7 @@ public class APITestService {
|
|||
test.setId(request.getId());
|
||||
test.setName(request.getName());
|
||||
test.setProjectId(request.getProjectId());
|
||||
test.setScenarioDefinition(request.getScenarioDefinition());
|
||||
test.setScenarioDefinition(JSONObject.toJSONString(request.getScenarioDefinition()));
|
||||
test.setUpdateTime(System.currentTimeMillis());
|
||||
test.setStatus(APITestStatus.Saved.name());
|
||||
apiTestMapper.updateByPrimaryKeySelective(test);
|
||||
|
@ -151,7 +164,7 @@ public class APITestService {
|
|||
test.setId(request.getId());
|
||||
test.setName(request.getName());
|
||||
test.setProjectId(request.getProjectId());
|
||||
test.setScenarioDefinition(request.getScenarioDefinition());
|
||||
test.setScenarioDefinition(JSONObject.toJSONString(request.getScenarioDefinition()));
|
||||
test.setCreateTime(System.currentTimeMillis());
|
||||
test.setUpdateTime(System.currentTimeMillis());
|
||||
test.setStatus(APITestStatus.Saved.name());
|
||||
|
@ -194,7 +207,4 @@ public class APITestService {
|
|||
}
|
||||
}
|
||||
|
||||
public List<ApiTest> getApiTestByProjectId(String projectId) {
|
||||
return extApiTestMapper.getApiTestByProjectId(projectId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@
|
|||
#{nodeId}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="request.status != null">
|
||||
and test_plan_test_case.status = #{request.status}
|
||||
</if>
|
||||
<if test="request.executor != null">
|
||||
and test_plan_test_case.executor = #{request.executor}
|
||||
</if>
|
||||
|
|
|
@ -27,4 +27,5 @@ public interface ExtUserRoleMapper {
|
|||
List<User> getBesideOrgMemberList(@Param("orgId") String orgId);
|
||||
|
||||
|
||||
List<User> getTestManagerAndTestUserList(@Param("request") QueryMemberRequest request);
|
||||
}
|
||||
|
|
|
@ -89,7 +89,12 @@
|
|||
join role r on r.id = ur.role_id
|
||||
where w.id = #{workspaceId} and ur.user_id = #{userId}
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<select id="getTestManagerAndTestUserList" resultType="io.metersphere.base.domain.User">
|
||||
SELECT distinct `user`.* FROM user_role INNER JOIN `user` ON user_role.user_id = `user`.id
|
||||
WHERE user_role.source_id = #{request.workspaceId} And user_role.role_id in ('test_manager', 'test_user')
|
||||
<if test="request.name != null">
|
||||
AND `user`.name like CONCAT('%', #{request.name},'%')
|
||||
</if>
|
||||
order by user_role.update_time desc
|
||||
</select>
|
||||
</mapper>
|
|
@ -0,0 +1,18 @@
|
|||
package io.metersphere.commons.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class MathUtils {
|
||||
|
||||
/**
|
||||
* 获取百分比
|
||||
* 保留一位小数
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static double getPercentWithDecimal(double value) {
|
||||
return new BigDecimal(value * 100)
|
||||
.setScale(1, BigDecimal.ROUND_HALF_UP)
|
||||
.doubleValue();
|
||||
}
|
||||
}
|
|
@ -274,4 +274,14 @@ public class UserController {
|
|||
return userService.updateUserPassword(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作空间成员用户 不分页
|
||||
*/
|
||||
@PostMapping("/ws/member/tester/list")
|
||||
@RequiresRoles(value = {RoleConstants.ORG_ADMIN, RoleConstants.TEST_MANAGER,
|
||||
RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
|
||||
public List<User> getTestManagerAndTestUserList(@RequestBody QueryMemberRequest request) {
|
||||
return userService.getTestManagerAndTestUserList(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -86,7 +86,12 @@ public class DockerTestEngine extends AbstractEngine {
|
|||
testRequest.setTestData(context.getTestData());
|
||||
testRequest.setEnv(context.getEnv());
|
||||
|
||||
try {
|
||||
restTemplate.postForObject(uri, testRequest, String.class);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
MSException.throwException(Translator.get("start_engine_fail"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -85,6 +85,7 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
if (nodeNameEquals(ele, HASH_TREE_ELEMENT)) {
|
||||
parseHashTree(ele);
|
||||
} else if (nodeNameEquals(ele, TEST_PLAN)) {
|
||||
processSetupTestPlan(ele);
|
||||
processTearDownTestPlan(ele);
|
||||
processCheckoutConfigTestElement(ele);
|
||||
processCheckoutDnsCacheManager(ele);
|
||||
|
@ -436,6 +437,87 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void processSetupTestPlan(Element ele) {
|
||||
Document document = ele.getOwnerDocument();
|
||||
Node hashTree = ele.getNextSibling();
|
||||
while (!(hashTree instanceof Element)) {
|
||||
hashTree = hashTree.getNextSibling();
|
||||
}
|
||||
|
||||
KafkaProperties kafkaProperties = CommonBeanFactory.getBean(KafkaProperties.class);
|
||||
String bootstrapServers = kafkaProperties.getBootstrapServers();
|
||||
String[] servers = StringUtils.split(bootstrapServers, ",");
|
||||
for (String s : servers) {
|
||||
String[] ipAndPort = StringUtils.split(s, ":");
|
||||
Element setupElement = document.createElement("SetupThreadGroup");
|
||||
setupElement.setAttribute("guiclass", "SetupThreadGroupGui");
|
||||
setupElement.setAttribute("testclass", "SetupThreadGroup");
|
||||
setupElement.setAttribute("testname", "setUp Thread Group");
|
||||
setupElement.setAttribute("enabled", "true");
|
||||
setupElement.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "stoptestnow"));
|
||||
Element elementProp = document.createElement("elementProp");
|
||||
elementProp.setAttribute("name", "ThreadGroup.main_controller");
|
||||
elementProp.setAttribute("elementType", "LoopController");
|
||||
elementProp.setAttribute("guiclass", "LoopControlPanel");
|
||||
elementProp.setAttribute("testclass", "LoopController");
|
||||
elementProp.setAttribute("testname", "Loop Controller");
|
||||
elementProp.setAttribute("enabled", "true");
|
||||
elementProp.appendChild(createBoolProp(document, "LoopController.continue_forever", false));
|
||||
elementProp.appendChild(createIntProp(document, "LoopController.loops", 1));
|
||||
setupElement.appendChild(elementProp);
|
||||
setupElement.appendChild(createStringProp(document, "ThreadGroup.num_threads", "1"));
|
||||
setupElement.appendChild(createStringProp(document, "ThreadGroup.ramp_time", "1"));
|
||||
setupElement.appendChild(createStringProp(document, "ThreadGroup.duration", ""));
|
||||
setupElement.appendChild(createStringProp(document, "ThreadGroup.delay", ""));
|
||||
setupElement.appendChild(createBoolProp(document, "ThreadGroup.scheduler", false));
|
||||
setupElement.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true));
|
||||
hashTree.appendChild(setupElement);
|
||||
|
||||
Element setupHashTree = document.createElement(HASH_TREE_ELEMENT);
|
||||
|
||||
Element tcpSampler = document.createElement("TCPSampler");
|
||||
tcpSampler.setAttribute("guiclass", "TCPSamplerGui");
|
||||
tcpSampler.setAttribute("testclass", "TCPSampler");
|
||||
tcpSampler.setAttribute("testname", "TCP Sampler");
|
||||
tcpSampler.setAttribute("enabled", "true");
|
||||
tcpSampler.appendChild(createStringProp(document, "TCPSampler.classname", "TCPClientImpl"));
|
||||
tcpSampler.appendChild(createStringProp(document, "TCPSampler.server", ipAndPort[0]));
|
||||
tcpSampler.appendChild(createBoolProp(document, "TCPSampler.reUseConnection", true));
|
||||
tcpSampler.appendChild(createStringProp(document, "TCPSampler.port", ipAndPort[1]));
|
||||
tcpSampler.appendChild(createBoolProp(document, "TCPSampler.nodelay", false));
|
||||
tcpSampler.appendChild(createStringProp(document, "TCPSampler.timeout", "100"));
|
||||
tcpSampler.appendChild(createStringProp(document, "TCPSampler.ctimeout", "100"));
|
||||
tcpSampler.appendChild(createStringProp(document, "TCPSampler.request", "1010"));
|
||||
tcpSampler.appendChild(createBoolProp(document, "TCPSampler.closeConnection", false));
|
||||
tcpSampler.appendChild(createStringProp(document, "TCPSampler.EolByte", "0"));
|
||||
tcpSampler.appendChild(createStringProp(document, "ConfigTestElement.username", ""));
|
||||
tcpSampler.appendChild(createStringProp(document, "ConfigTestElement.password", ""));
|
||||
|
||||
Element tcpSamplerHashTree = document.createElement(HASH_TREE_ELEMENT);
|
||||
|
||||
Element responseAssertion = document.createElement("ResponseAssertion");
|
||||
responseAssertion.setAttribute("guiclass", "AssertionGui");
|
||||
responseAssertion.setAttribute("testclass", "ResponseAssertion");
|
||||
responseAssertion.setAttribute("testname", "Response Assertion");
|
||||
responseAssertion.setAttribute("enabled", "true");
|
||||
Element collectionProp = document.createElement("collectionProp");
|
||||
collectionProp.setAttribute("name", "Asserion.test_strings");
|
||||
collectionProp.appendChild(createStringProp(document, "49586", "200"));
|
||||
responseAssertion.appendChild(collectionProp);
|
||||
responseAssertion.appendChild(createStringProp(document, "Assertion.custom_message", ""));
|
||||
responseAssertion.appendChild(createStringProp(document, "Assertion.test_field", "Assertion.response_code"));
|
||||
responseAssertion.appendChild(createBoolProp(document, "Assertion.assume_success", false));
|
||||
responseAssertion.appendChild(createIntProp(document, "Assertion.test_type", 8));
|
||||
tcpSamplerHashTree.appendChild(responseAssertion);
|
||||
// 添加空的hashtree
|
||||
tcpSamplerHashTree.appendChild(document.createElement(HASH_TREE_ELEMENT));
|
||||
|
||||
setupHashTree.appendChild(tcpSampler);
|
||||
setupHashTree.appendChild(tcpSamplerHashTree);
|
||||
|
||||
hashTree.appendChild(setupHashTree);
|
||||
}
|
||||
}
|
||||
|
||||
private void processTearDownTestPlan(Element ele) {
|
||||
/*<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>*/
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.metersphere.commons.exception.MSException;
|
|||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.ServiceUtils;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.config.KafkaProperties;
|
||||
import io.metersphere.controller.request.OrderRequest;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import io.metersphere.dto.LoadTestDTO;
|
||||
|
@ -28,6 +29,9 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
|
@ -62,6 +66,8 @@ public class PerformanceTestService {
|
|||
private TestResourceService testResourceService;
|
||||
@Resource
|
||||
private ReportService reportService;
|
||||
@Resource
|
||||
private KafkaProperties kafkaProperties;
|
||||
|
||||
public List<LoadTestDTO> list(QueryTestPlanRequest request) {
|
||||
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
|
||||
|
@ -197,6 +203,8 @@ public class PerformanceTestService {
|
|||
if (StringUtils.equalsAny(loadTest.getStatus(), PerformanceTestStatus.Running.name(), PerformanceTestStatus.Starting.name())) {
|
||||
MSException.throwException(Translator.get("load_test_is_running"));
|
||||
}
|
||||
// check kafka
|
||||
checkKafka();
|
||||
|
||||
LogUtil.info("Load test started " + loadTest.getName());
|
||||
// engine type (NODE)
|
||||
|
@ -212,6 +220,31 @@ public class PerformanceTestService {
|
|||
return engine.getReportId();
|
||||
}
|
||||
|
||||
private void checkKafka() {
|
||||
String bootstrapServers = kafkaProperties.getBootstrapServers();
|
||||
String[] servers = StringUtils.split(bootstrapServers, ",");
|
||||
try {
|
||||
for (String s : servers) {
|
||||
String[] ipAndPort = s.split(":");
|
||||
//1,建立tcp
|
||||
String ip = ipAndPort[0];
|
||||
int port = Integer.parseInt(ipAndPort[1]);
|
||||
Socket soc = new Socket();
|
||||
soc.connect(new InetSocketAddress(ip, port), 1000); // 1s timeout
|
||||
//2.输入内容
|
||||
String content = "1010";
|
||||
byte[] bs = content.getBytes();
|
||||
OutputStream os = soc.getOutputStream();
|
||||
os.write(bs);
|
||||
//3.关闭
|
||||
soc.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
MSException.throwException(Translator.get("load_test_kafka_invalid"));
|
||||
}
|
||||
}
|
||||
|
||||
private void startEngine(LoadTestWithBLOBs loadTest, Engine engine) {
|
||||
LoadTestReport testReport = new LoadTestReport();
|
||||
testReport.setId(engine.getReportId());
|
||||
|
|
|
@ -457,4 +457,7 @@ public class UserService {
|
|||
return extUserMapper.getDefaultLanguage(key);
|
||||
}
|
||||
|
||||
public List<User> getTestManagerAndTestUserList(QueryMemberRequest request) {
|
||||
return extUserRoleMapper.getTestManagerAndTestUserList(request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package io.metersphere.track.Factory;
|
||||
|
||||
import io.metersphere.track.domain.*;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ReportComponentFactory {
|
||||
|
||||
public static ReportComponent createComponent(String componentId, TestPlanDTO testPlan) {
|
||||
if (StringUtils.equals("1", componentId)) {
|
||||
return new ReportBaseInfoComponent(testPlan);
|
||||
} else if (StringUtils.equals("2", componentId)) {
|
||||
return new ReportResultComponent(testPlan);
|
||||
} else if (StringUtils.equals("3", componentId)) {
|
||||
return new ReportResultChartComponent(testPlan);
|
||||
} else if (StringUtils.equals("4", componentId)) {
|
||||
return new ReportFailureResultComponent(testPlan);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<ReportComponent> createComponents(List<String> componentIds, TestPlanDTO testPlan) {
|
||||
List<ReportComponent> components = new ArrayList<>();
|
||||
componentIds.forEach(id -> {
|
||||
ReportComponent component = createComponent(id, testPlan);
|
||||
if (component != null) {
|
||||
components.add(component);
|
||||
}
|
||||
});
|
||||
return components;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package io.metersphere.track.domain;
|
||||
|
||||
import io.metersphere.track.dto.TestCaseReportMetricDTO;
|
||||
import io.metersphere.track.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ReportBaseInfoComponent extends ReportComponent {
|
||||
private Set<String> executorsSet = new HashSet<>();
|
||||
|
||||
public ReportBaseInfoComponent(TestPlanDTO testPlan) {
|
||||
super(testPlan);
|
||||
componentId = "1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readRecord(TestPlanCaseDTO testCase) {
|
||||
executorsSet.add(testCase.getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) {
|
||||
testCaseReportMetric.setProjectName(testPlan.getProjectName());
|
||||
testCaseReportMetric.setPrincipal(testPlan.getPrincipal());
|
||||
testCaseReportMetric.setExecutors(new ArrayList<>(this.executorsSet));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.track.domain;
|
||||
|
||||
import io.metersphere.track.dto.TestCaseReportMetricDTO;
|
||||
import io.metersphere.track.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
|
||||
public abstract class ReportComponent {
|
||||
protected String componentId;
|
||||
protected TestPlanDTO testPlan;
|
||||
public ReportComponent(TestPlanDTO testPlan) {
|
||||
this.testPlan = testPlan;
|
||||
}
|
||||
public abstract void readRecord(TestPlanCaseDTO testCase);
|
||||
public abstract void afterBuild(TestCaseReportMetricDTO testCaseReportMetric);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package io.metersphere.track.domain;
|
||||
|
||||
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
|
||||
import io.metersphere.track.dto.TestCaseReportMetricDTO;
|
||||
import io.metersphere.track.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ReportFailureResultComponent extends ReportComponent {
|
||||
private List<TestPlanCaseDTO> failureTestCases = new ArrayList<>();
|
||||
public ReportFailureResultComponent(TestPlanDTO testPlan) {
|
||||
super(testPlan);
|
||||
componentId = "4";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readRecord(TestPlanCaseDTO testCase) {
|
||||
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Failure.name())) {
|
||||
this.failureTestCases.add(testCase);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) {
|
||||
testCaseReportMetric.setFailureTestCases(failureTestCases);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package io.metersphere.track.domain;
|
||||
|
||||
import io.metersphere.track.dto.TestCaseReportMetricDTO;
|
||||
import io.metersphere.track.dto.TestCaseReportStatusResultDTO;
|
||||
import io.metersphere.track.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ReportResultChartComponent extends ReportComponent {
|
||||
Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap = new HashMap<>();
|
||||
|
||||
public ReportResultChartComponent(TestPlanDTO testPlan) {
|
||||
super(testPlan);
|
||||
componentId = "3";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void readRecord(TestPlanCaseDTO testCase) {
|
||||
getStatusResultMap(reportStatusResultMap, testCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) {
|
||||
testCaseReportMetric.setExecuteResult(new ArrayList<>(reportStatusResultMap.values()));
|
||||
}
|
||||
|
||||
private void getStatusResultMap(Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap, TestPlanCaseDTO testCase) {
|
||||
TestCaseReportStatusResultDTO statusResult = reportStatusResultMap.get(testCase.getStatus());
|
||||
if (statusResult == null) {
|
||||
statusResult = new TestCaseReportStatusResultDTO();
|
||||
statusResult.setStatus(testCase.getStatus());
|
||||
statusResult.setCount(0);
|
||||
}
|
||||
statusResult.setCount(statusResult.getCount() + 1);
|
||||
reportStatusResultMap.put(testCase.getStatus(), statusResult);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package io.metersphere.track.domain;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.base.domain.TestCaseNode;
|
||||
import io.metersphere.base.domain.TestCaseNodeExample;
|
||||
import io.metersphere.base.mapper.TestCaseNodeMapper;
|
||||
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.MathUtils;
|
||||
import io.metersphere.track.dto.*;
|
||||
import io.metersphere.track.service.TestCaseNodeService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ReportResultComponent extends ReportComponent {
|
||||
|
||||
private List<TestCaseNodeDTO> nodeTrees = new ArrayList<>();
|
||||
private Map<String, Set<String>> childIdMap = new HashMap<>();
|
||||
private Map<String, TestCaseReportModuleResultDTO> moduleResultMap = new HashMap<>();
|
||||
|
||||
public ReportResultComponent(TestPlanDTO testPlan) {
|
||||
super(testPlan);
|
||||
componentId = "2";
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
TestCaseNodeService testCaseNodeService = (TestCaseNodeService) CommonBeanFactory.getBean("testCaseNodeService");
|
||||
TestCaseNodeMapper testCaseNodeMapper = (TestCaseNodeMapper) CommonBeanFactory.getBean("testCaseNodeMapper");
|
||||
TestCaseNodeExample testCaseNodeExample = new TestCaseNodeExample();
|
||||
testCaseNodeExample.createCriteria().andProjectIdEqualTo(testPlan.getProjectId());
|
||||
List<TestCaseNode> nodes = testCaseNodeMapper.selectByExample(testCaseNodeExample);
|
||||
nodeTrees = testCaseNodeService.getNodeTrees(nodes);
|
||||
nodeTrees.forEach(item -> {
|
||||
Set<String> childIds = new HashSet<>();
|
||||
getChildIds(item, childIds);
|
||||
childIdMap.put(item.getId(), childIds);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readRecord(TestPlanCaseDTO testCase) {
|
||||
getModuleResultMap(childIdMap, moduleResultMap, testCase, nodeTrees);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) {
|
||||
|
||||
nodeTrees.forEach(rootNode -> {
|
||||
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNode.getId());
|
||||
if (moduleResult != null) {
|
||||
moduleResult.setModuleName(rootNode.getName());
|
||||
}
|
||||
});
|
||||
|
||||
for (TestCaseReportModuleResultDTO moduleResult : moduleResultMap.values()) {
|
||||
moduleResult.setPassRate(MathUtils.getPercentWithDecimal(moduleResult.getPassCount()*1.0f/moduleResult.getCaseCount()));
|
||||
if (moduleResult.getCaseCount() <= 0) {
|
||||
moduleResultMap.remove(moduleResult.getModuleId());
|
||||
}
|
||||
}
|
||||
testCaseReportMetric.setModuleExecuteResult(new ArrayList<>(moduleResultMap.values()));
|
||||
}
|
||||
|
||||
private void getChildIds(TestCaseNodeDTO rootNode, Set<String> childIds) {
|
||||
|
||||
childIds.add(rootNode.getId());
|
||||
|
||||
List<TestCaseNodeDTO> children = rootNode.getChildren();
|
||||
|
||||
if(children != null) {
|
||||
Iterator<TestCaseNodeDTO> iterator = children.iterator();
|
||||
while(iterator.hasNext()){
|
||||
getChildIds(iterator.next(), childIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getModuleResultMap(Map<String, Set<String>> childIdMap, Map<String, TestCaseReportModuleResultDTO> moduleResultMap, TestPlanCaseDTO testCase, List<TestCaseNodeDTO> nodeTrees) {
|
||||
childIdMap.forEach((rootNodeId, childIds) -> {
|
||||
if (childIds.contains(testCase.getNodeId())) {
|
||||
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNodeId);
|
||||
if (moduleResult == null) {
|
||||
moduleResult = new TestCaseReportModuleResultDTO();
|
||||
moduleResult.setCaseCount(0);
|
||||
moduleResult.setPassCount(0);
|
||||
moduleResult.setIssuesCount(0);
|
||||
moduleResult.setModuleId(rootNodeId);
|
||||
}
|
||||
moduleResult.setCaseCount(moduleResult.getCaseCount() + 1);
|
||||
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Pass.name())) {
|
||||
moduleResult.setPassCount(moduleResult.getPassCount() + 1);
|
||||
}
|
||||
if (StringUtils.isNotBlank(testCase.getIssues())) {
|
||||
if (JSON.parseObject(testCase.getIssues()).getBoolean("hasIssues")) {
|
||||
moduleResult.setIssuesCount(moduleResult.getIssuesCount() + 1);
|
||||
};
|
||||
}
|
||||
moduleResultMap.put(rootNodeId, moduleResult);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
package io.metersphere.track.dto;
|
||||
|
||||
import io.metersphere.track.domain.ReportBaseInfoComponent;
|
||||
import io.metersphere.track.domain.ReportFailureResultComponent;
|
||||
import io.metersphere.track.domain.ReportResultChartComponent;
|
||||
import io.metersphere.track.domain.ReportResultComponent;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
|
@ -11,6 +15,7 @@ public class TestCaseReportMetricDTO {
|
|||
|
||||
private List<TestCaseReportStatusResultDTO> executeResult;
|
||||
private List<TestCaseReportModuleResultDTO> moduleExecuteResult;
|
||||
private List<TestPlanCaseDTO> failureTestCases;
|
||||
private List<String> executors;
|
||||
private String principal;
|
||||
private Long startTime;
|
||||
|
|
|
@ -25,4 +25,6 @@ public class QueryTestPlanCaseRequest extends TestPlanTestCase {
|
|||
private String workspaceId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String status;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,10 @@ package io.metersphere.track.service;
|
|||
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.TestCaseMapper;
|
||||
import io.metersphere.base.mapper.TestCaseNodeMapper;
|
||||
import io.metersphere.base.mapper.TestPlanMapper;
|
||||
import io.metersphere.base.mapper.TestPlanTestCaseMapper;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtProjectMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
|
||||
|
@ -14,10 +13,14 @@ import io.metersphere.commons.constants.TestPlanStatus;
|
|||
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
import io.metersphere.commons.utils.MathUtils;
|
||||
import io.metersphere.commons.utils.ServiceUtils;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.controller.request.ProjectRequest;
|
||||
import io.metersphere.controller.request.member.QueryMemberRequest;
|
||||
import io.metersphere.dto.ProjectDTO;
|
||||
import io.metersphere.track.Factory.ReportComponentFactory;
|
||||
import io.metersphere.track.domain.ReportComponent;
|
||||
import io.metersphere.track.dto.*;
|
||||
import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
|
@ -27,6 +30,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
@ -58,15 +62,16 @@ public class TestPlanService {
|
|||
@Resource
|
||||
SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
@Lazy
|
||||
@Resource
|
||||
TestCaseNodeMapper testCaseNodeMapper;
|
||||
|
||||
@Resource
|
||||
TestCaseNodeService testCaseNodeService;
|
||||
TestPlanTestCaseService testPlanTestCaseService;
|
||||
|
||||
@Resource
|
||||
ExtProjectMapper extProjectMapper;
|
||||
|
||||
@Resource
|
||||
TestCaseReportMapper testCaseReportMapper;
|
||||
|
||||
public void addTestPlan(TestPlan testPlan) {
|
||||
if (getTestPlanByName(testPlan.getName()).size() > 0) {
|
||||
MSException.throwException(Translator.get("plan_name_already_exists"));
|
||||
|
@ -226,23 +231,17 @@ public class TestPlanService {
|
|||
}
|
||||
});
|
||||
}
|
||||
testPlan.setPassRate(getPercentWithTwoDecimals(testPlan.getTested() == 0 ? 0 : testPlan.getPassed()*1.0/testPlan.getTested()));
|
||||
testPlan.setTestRate(getPercentWithTwoDecimals(testPlan.getTotal() == 0 ? 0 : testPlan.getTested()*1.0/testPlan.getTotal()));
|
||||
testPlan.setPassRate(MathUtils.getPercentWithDecimal(testPlan.getTested() == 0 ? 0 : testPlan.getPassed()*1.0/testPlan.getTested()));
|
||||
testPlan.setTestRate(MathUtils.getPercentWithDecimal(testPlan.getTotal() == 0 ? 0 : testPlan.getTested()*1.0/testPlan.getTotal()));
|
||||
});
|
||||
|
||||
return testPlans;
|
||||
}
|
||||
|
||||
private double getPercentWithTwoDecimals(double value) {
|
||||
return new BigDecimal(value * 100)
|
||||
.setScale(1, BigDecimal.ROUND_HALF_UP)
|
||||
.doubleValue();
|
||||
}
|
||||
|
||||
public List<TestPlanCaseDTO> listTestCaseByPlanId(String planId) {
|
||||
QueryTestPlanCaseRequest request = new QueryTestPlanCaseRequest();
|
||||
request.setPlanId(planId);
|
||||
return extTestPlanTestCaseMapper.list(request);
|
||||
return testPlanTestCaseService.list(request);
|
||||
}
|
||||
|
||||
public List<TestPlanCaseDTO> listTestCaseByProjectIds(List<String> projectIds) {
|
||||
|
@ -255,107 +254,26 @@ public class TestPlanService {
|
|||
|
||||
QueryTestPlanRequest queryTestPlanRequest = new QueryTestPlanRequest();
|
||||
queryTestPlanRequest.setId(planId);
|
||||
|
||||
TestPlanDTO testPlan = extTestPlanMapper.list(queryTestPlanRequest).get(0);
|
||||
TestCaseReport testCaseReport = testCaseReportMapper.selectByPrimaryKey(testPlan.getReportId());
|
||||
JSONObject content = JSONObject.parseObject(testCaseReport.getContent());
|
||||
JSONArray componentIds = content.getJSONArray("components");
|
||||
|
||||
Set<String> executors = new HashSet<>();
|
||||
Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap = new HashMap<>();
|
||||
|
||||
TestCaseNodeExample testCaseNodeExample = new TestCaseNodeExample();
|
||||
testCaseNodeExample.createCriteria().andProjectIdEqualTo(testPlan.getProjectId());
|
||||
List<TestCaseNode> nodes = testCaseNodeMapper.selectByExample(testCaseNodeExample);
|
||||
|
||||
List<TestCaseNodeDTO> nodeTrees = testCaseNodeService.getNodeTrees(nodes);
|
||||
|
||||
Map<String, Set<String>> childIdMap = new HashMap<>();
|
||||
nodeTrees.forEach(item -> {
|
||||
Set<String> childIds = new HashSet<>();
|
||||
getChildIds(item, childIds);
|
||||
childIdMap.put(item.getId(), childIds);
|
||||
});
|
||||
List<ReportComponent> components = ReportComponentFactory.createComponents(componentIds.toJavaList(String.class), testPlan);
|
||||
|
||||
List<TestPlanCaseDTO> testPlanTestCases = listTestCaseByPlanId(planId);
|
||||
|
||||
Map<String, TestCaseReportModuleResultDTO> moduleResultMap = new HashMap<>();
|
||||
|
||||
for (TestPlanCaseDTO testCase: testPlanTestCases) {
|
||||
executors.add(testCase.getExecutor());
|
||||
getStatusResultMap(reportStatusResultMap, testCase);
|
||||
getModuleResultMap(childIdMap, moduleResultMap, testCase, nodeTrees);
|
||||
}
|
||||
|
||||
nodeTrees.forEach(rootNode -> {
|
||||
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNode.getId());
|
||||
if (moduleResult != null) {
|
||||
moduleResult.setModuleName(rootNode.getName());
|
||||
}
|
||||
components.forEach(component -> {
|
||||
component.readRecord(testCase);
|
||||
});
|
||||
|
||||
for (TestCaseReportModuleResultDTO moduleResult : moduleResultMap.values()) {
|
||||
moduleResult.setPassRate(getPercentWithTwoDecimals(moduleResult.getPassCount()*1.0f/moduleResult.getCaseCount()));
|
||||
if (moduleResult.getCaseCount() <= 0) {
|
||||
moduleResultMap.remove(moduleResult.getModuleId());
|
||||
}
|
||||
}
|
||||
|
||||
TestCaseReportMetricDTO testCaseReportMetricDTO = new TestCaseReportMetricDTO();
|
||||
testCaseReportMetricDTO.setProjectName(testPlan.getProjectName());
|
||||
testCaseReportMetricDTO.setPrincipal(testPlan.getPrincipal());
|
||||
testCaseReportMetricDTO.setExecutors(new ArrayList<>(executors));
|
||||
testCaseReportMetricDTO.setExecuteResult(new ArrayList<>(reportStatusResultMap.values()));
|
||||
testCaseReportMetricDTO.setModuleExecuteResult(new ArrayList<>(moduleResultMap.values()));
|
||||
|
||||
return testCaseReportMetricDTO;
|
||||
}
|
||||
|
||||
private void getStatusResultMap(Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap, TestPlanCaseDTO testCase) {
|
||||
TestCaseReportStatusResultDTO statusResult = reportStatusResultMap.get(testCase.getStatus());
|
||||
if (statusResult == null) {
|
||||
statusResult = new TestCaseReportStatusResultDTO();
|
||||
statusResult.setStatus(testCase.getStatus());
|
||||
statusResult.setCount(0);
|
||||
}
|
||||
statusResult.setCount(statusResult.getCount() + 1);
|
||||
reportStatusResultMap.put(testCase.getStatus(), statusResult);
|
||||
}
|
||||
|
||||
private void getModuleResultMap(Map<String, Set<String>> childIdMap, Map<String, TestCaseReportModuleResultDTO> moduleResultMap, TestPlanCaseDTO testCase, List<TestCaseNodeDTO> nodeTrees) {
|
||||
childIdMap.forEach((rootNodeId, childIds) -> {
|
||||
if (childIds.contains(testCase.getNodeId())) {
|
||||
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNodeId);
|
||||
if (moduleResult == null) {
|
||||
moduleResult = new TestCaseReportModuleResultDTO();
|
||||
moduleResult.setCaseCount(0);
|
||||
moduleResult.setPassCount(0);
|
||||
moduleResult.setIssuesCount(0);
|
||||
moduleResult.setModuleId(rootNodeId);
|
||||
}
|
||||
moduleResult.setCaseCount(moduleResult.getCaseCount() + 1);
|
||||
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Pass.name())) {
|
||||
moduleResult.setPassCount(moduleResult.getPassCount() + 1);
|
||||
}
|
||||
if (StringUtils.isNotBlank(testCase.getIssues())) {
|
||||
if (JSON.parseObject(testCase.getIssues()).getBoolean("hasIssues")) {
|
||||
moduleResult.setIssuesCount(moduleResult.getIssuesCount() + 1);
|
||||
};
|
||||
}
|
||||
moduleResultMap.put(rootNodeId, moduleResult);
|
||||
return;
|
||||
}
|
||||
components.forEach(component -> {
|
||||
component.afterBuild(testCaseReportMetricDTO);
|
||||
});
|
||||
}
|
||||
|
||||
private void getChildIds(TestCaseNodeDTO rootNode, Set<String> childIds) {
|
||||
|
||||
childIds.add(rootNode.getId());
|
||||
|
||||
List<TestCaseNodeDTO> children = rootNode.getChildren();
|
||||
|
||||
if(children != null) {
|
||||
Iterator<TestCaseNodeDTO> iterator = children.iterator();
|
||||
while(iterator.hasNext()){
|
||||
getChildIds(iterator.next(), childIds);
|
||||
}
|
||||
}
|
||||
return testCaseReportMetricDTO;
|
||||
}
|
||||
|
||||
public List<TestPlan> getTestPlanByIds(List<String> planIds) {
|
||||
|
|
|
@ -64,5 +64,4 @@ kafka.ssl.provider=
|
|||
kafka.ssl.truststore-type=
|
||||
|
||||
# jmeter
|
||||
jmeter.image=registry.fit2cloud.com/metersphere/jmeter-master:0.0.4
|
||||
jmeter.home=/opt/jmeter
|
|
@ -34,6 +34,7 @@ run_load_test_file_not_found=Unable to run test, unable to get test file meta in
|
|||
run_load_test_file_content_not_found=Cannot run test, cannot get test file content, test ID=
|
||||
run_load_test_file_init_error=Failed to run test, failed to initialize run environment, test ID=
|
||||
load_test_is_running=Load test is running, please wait.
|
||||
load_test_kafka_invalid=Kafka is not available, please check the configuration
|
||||
cannot_edit_load_test_running=Cannot modify the running test
|
||||
test_not_found=Test cannot be found:
|
||||
test_not_running=Test is not running
|
||||
|
|
|
@ -34,6 +34,7 @@ run_load_test_file_not_found=无法运行测试,无法获取测试文件元信
|
|||
run_load_test_file_content_not_found=无法运行测试,无法获取测试文件内容,测试ID:
|
||||
run_load_test_file_init_error=无法运行测试,初始化运行环境失败,测试ID:
|
||||
load_test_is_running=测试正在运行, 请等待
|
||||
load_test_kafka_invalid=Kafka 不可用,请检查配置
|
||||
cannot_edit_load_test_running=不能修改正在运行的测试
|
||||
test_not_found=测试不存在:
|
||||
test_not_running=测试未运行
|
||||
|
|
|
@ -34,6 +34,7 @@ run_load_test_file_not_found=無法運行測試,無法獲取測試文件元信
|
|||
run_load_test_file_content_not_found=無法運行測試,無法獲取測試文件內容,測試ID:
|
||||
run_load_test_file_init_error=無法運行測試,初始化運行環境失敗,測試ID:
|
||||
load_test_is_running=測試正在運行, 請等待
|
||||
load_test_kafka_invalid=Kafka 不可用,請檢查配置
|
||||
cannot_edit_load_test_running=不能修改正在運行的測試
|
||||
test_not_found=測試不存在:
|
||||
test_not_running=測試未運行
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-row type="flex" justify="center">
|
||||
<ms-create-test v-permission="['test_manager','test_user']" :to="'/api/test/create'"/>
|
||||
<ms-create-test :to="'/api/test/create'"/>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="8"/>
|
||||
|
|
|
@ -63,7 +63,12 @@
|
|||
this.$get(url, response => {
|
||||
this.report = response.data || {};
|
||||
if (this.isNotRunning) {
|
||||
try {
|
||||
this.content = JSON.parse(this.report.content);
|
||||
} catch (e) {
|
||||
console.log(this.report.content)
|
||||
throw e;
|
||||
}
|
||||
this.getFails();
|
||||
this.loading = false;
|
||||
} else {
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<el-container class="test-container" v-loading="result.loading">
|
||||
<el-header>
|
||||
<el-row type="flex" align="middle">
|
||||
<el-input :disabled="isReadOnly" class="test-name" v-model="test.name" maxlength="60" :placeholder="$t('api_test.input_name')"
|
||||
<el-input :disabled="isReadOnly" class="test-name" v-model="test.name" maxlength="60"
|
||||
:placeholder="$t('api_test.input_name')"
|
||||
show-word-limit>
|
||||
<el-select :disabled="isReadOnly" class="test-project" v-model="test.projectId" slot="prepend"
|
||||
:placeholder="$t('api_test.select_project')">
|
||||
|
@ -17,7 +18,8 @@
|
|||
{{$t('commons.save')}}
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" plain v-if="!isShowRun" :disabled="isDisabled || isReadOnly" @click="saveRunTest">
|
||||
<el-button type="primary" plain v-if="!isShowRun" :disabled="isDisabled || isReadOnly"
|
||||
@click="saveRunTest">
|
||||
{{$t('load_test.save_and_run')}}
|
||||
</el-button>
|
||||
|
||||
|
@ -25,7 +27,8 @@
|
|||
{{$t('api_test.run')}}
|
||||
</el-button>
|
||||
|
||||
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
|
||||
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}
|
||||
</el-button>
|
||||
|
||||
<el-dropdown trigger="click" @command="handleCommand">
|
||||
<el-button class="el-dropdown-link more" icon="el-icon-more" plain/>
|
||||
|
@ -36,6 +39,9 @@
|
|||
<el-dropdown-item command="performance" :disabled="create || isReadOnly">
|
||||
{{$t('api_test.create_performance_test')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="export" :disabled="isDisabled || isReadOnly">
|
||||
{{$t('api_test.export_config')}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
|
@ -54,7 +60,7 @@
|
|||
import {Test} from "./model/ScenarioModel"
|
||||
import MsApiReportStatus from "../report/ApiReportStatus";
|
||||
import MsApiReportDialog from "./ApiReportDialog";
|
||||
import {checkoutTestManagerOrTestUser} from "../../../../common/js/utils";
|
||||
import {checkoutTestManagerOrTestUser, downloadFile} from "../../../../common/js/utils";
|
||||
|
||||
export default {
|
||||
name: "MsApiTestConfig",
|
||||
|
@ -165,13 +171,7 @@
|
|||
},
|
||||
getOptions(url) {
|
||||
let formData = new FormData();
|
||||
let request = {
|
||||
id: this.test.id,
|
||||
projectId: this.test.projectId,
|
||||
name: this.test.name,
|
||||
scenarioDefinition: JSON.stringify(this.test.scenarioDefinition)
|
||||
}
|
||||
let requestJson = JSON.stringify(request);
|
||||
let requestJson = JSON.stringify(this.test);
|
||||
|
||||
formData.append('request', new Blob([requestJson], {
|
||||
type: "application/json"
|
||||
|
@ -204,6 +204,9 @@
|
|||
path: "/performance/test/create"
|
||||
})
|
||||
break;
|
||||
case "export":
|
||||
downloadFile(this.test.name + ".json", this.test.export());
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
});
|
||||
},
|
||||
handleCopy(test) {
|
||||
this.result = this.$post("/api/copy", {id: test.id}, () => {
|
||||
this.result = this.$post("/api/copy", {projectId: test.projectId, id: test.id, name: test.name}, () => {
|
||||
this.$success(this.$t('commons.copy_success'));
|
||||
this.search();
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
:placeholder="$t('api_test.key')" show-word-limit/>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-input :disabled="isReadOnly" v-model="item.value" size="small" maxlength="100" @change="change"
|
||||
<el-input :disabled="isReadOnly" v-model="item.value" size="small" maxlength="500" @change="change"
|
||||
:placeholder="$t('api_test.value')" show-word-limit/>
|
||||
</el-col>
|
||||
<el-col class="kv-delete">
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('api_test.request.url')" prop="url">
|
||||
<el-input :disabled="isReadOnly" v-model="request.url" maxlength="100" :placeholder="$t('api_test.request.url_description')"
|
||||
@change="urlChange" clearable>
|
||||
<el-select :disabled="isReadOnly" v-model="request.method" slot="prepend" class="request-method-select" @change="methodChange">
|
||||
<el-input :disabled="isReadOnly" v-model="request.url" maxlength="500"
|
||||
:placeholder="$t('api_test.request.url_description')" @change="urlChange" clearable>
|
||||
<el-select :disabled="isReadOnly" v-model="request.method" slot="prepend" class="request-method-select"
|
||||
@change="methodChange">
|
||||
<el-option label="GET" value="GET"/>
|
||||
<el-option label="POST" value="POST"/>
|
||||
<el-option label="PUT" value="PUT"/>
|
||||
|
@ -22,8 +23,8 @@
|
|||
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="request.parameters" :description="$t('api_test.request.parameters_desc')"
|
||||
@change="parametersChange"/>
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="request.parameters"
|
||||
:description="$t('api_test.request.parameters_desc')" @change="parametersChange"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="request.headers"/>
|
||||
|
@ -74,7 +75,7 @@
|
|||
{max: 100, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'}
|
||||
],
|
||||
url: [
|
||||
{max: 100, required: true, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'},
|
||||
{max: 500, required: true, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'},
|
||||
{validator: validateURL, trigger: 'blur'}
|
||||
]
|
||||
}
|
||||
|
@ -96,7 +97,7 @@
|
|||
// 添加一个空的,用于填写
|
||||
parameters.push(new KeyValue());
|
||||
this.request.parameters = parameters;
|
||||
this.request.url = url.toString();
|
||||
this.request.url = this.getURL(url);
|
||||
} catch (e) {
|
||||
this.$error(this.$t('api_test.request.url_invalid'), 2000)
|
||||
}
|
||||
|
@ -116,7 +117,7 @@
|
|||
url.searchParams.append(parameter.name, parameter.value);
|
||||
}
|
||||
})
|
||||
this.request.url = url.toString();
|
||||
this.request.url = this.getURL(url);
|
||||
},
|
||||
addProtocol(url) {
|
||||
if (url) {
|
||||
|
@ -125,6 +126,9 @@
|
|||
}
|
||||
}
|
||||
return url;
|
||||
},
|
||||
getURL(url) {
|
||||
return decodeURIComponent(url.origin + url.pathname) + "?" + url.searchParams.toString();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
|
||||
<script>
|
||||
|
||||
import {ResponseTime} from "../../model/ScenarioModel";
|
||||
import {Duration} from "../../model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertionResponseTime",
|
||||
name: "MsApiAssertionDuration",
|
||||
|
||||
props: {
|
||||
duration: ResponseTime,
|
||||
duration: Duration,
|
||||
value: [Number, String],
|
||||
edit: Boolean,
|
||||
callback: Function,
|
|
@ -6,14 +6,14 @@
|
|||
size="small">
|
||||
<el-option :label="$t('api_test.request.assertions.text')" :value="options.TEXT"/>
|
||||
<el-option :label="$t('api_test.request.assertions.regex')" :value="options.REGEX"/>
|
||||
<el-option :label="$t('api_test.request.assertions.response_time')" :value="options.RESPONSE_TIME"/>
|
||||
<el-option :label="$t('api_test.request.assertions.response_time')" :value="options.DURATION"/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<ms-api-assertion-text :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.TEXT" :callback="after"/>
|
||||
<ms-api-assertion-regex :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.REGEX" :callback="after"/>
|
||||
<ms-api-assertion-response-time :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
|
||||
v-if="type === options.RESPONSE_TIME" :callback="after"/>
|
||||
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
|
||||
v-if="type === options.DURATION" :callback="after"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
@ -24,14 +24,14 @@
|
|||
<script>
|
||||
import MsApiAssertionText from "./ApiAssertionText";
|
||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
|
||||
import {ASSERTION_TYPE, Assertions, ResponseTime} from "../../model/ScenarioModel";
|
||||
import MsApiAssertionDuration from "./ApiAssertionDuration";
|
||||
import {ASSERTION_TYPE, Assertions} from "../../model/ScenarioModel";
|
||||
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertions",
|
||||
|
||||
components: {MsApiAssertionsEdit, MsApiAssertionResponseTime, MsApiAssertionRegex, MsApiAssertionText},
|
||||
components: {MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText},
|
||||
|
||||
props: {
|
||||
assertions: Assertions,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<div>
|
||||
{{$t("api_test.request.assertions.response_time")}}
|
||||
</div>
|
||||
<ms-api-assertion-response-time :is-read-only="isReadOnly" v-model="assertions.duration.value" :duration="assertions.duration" :edit="true"/>
|
||||
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="assertions.duration.value" :duration="assertions.duration" :edit="true"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -21,13 +21,13 @@
|
|||
|
||||
<script>
|
||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
|
||||
import MsApiAssertionDuration from "./ApiAssertionDuration";
|
||||
import {Assertions} from "../../model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertionsEdit",
|
||||
|
||||
components: {MsApiAssertionResponseTime, MsApiAssertionRegex},
|
||||
components: {MsApiAssertionDuration, MsApiAssertionRegex},
|
||||
|
||||
props: {
|
||||
assertions: Assertions,
|
||||
|
|
|
@ -33,7 +33,7 @@ export class Element {
|
|||
}
|
||||
|
||||
commonValue(tag, name, value, defaultValue) {
|
||||
let v = this.getDefault(value, defaultValue)
|
||||
let v = this.getDefault(value, defaultValue);
|
||||
return this.add(new Element(tag, {name: name}, v));
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,11 @@ export class Element {
|
|||
return this.isEmptyValue() && this.isEmptyElement();
|
||||
}
|
||||
|
||||
replace(str) {
|
||||
if (!str || !(typeof str === 'string')) return str;
|
||||
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'").replace(/"/g, """);
|
||||
}
|
||||
|
||||
toXML(indent) {
|
||||
if (indent) {
|
||||
this.indent = indent;
|
||||
|
@ -85,10 +90,10 @@ export class Element {
|
|||
}
|
||||
|
||||
start() {
|
||||
let str = this.indent + '<' + this.name;
|
||||
let str = this.indent + '<' + this.replace(this.name);
|
||||
for (let key in this.attributes) {
|
||||
if (this.attributes.hasOwnProperty(key)) {
|
||||
str += ' ' + key + '="' + this.attributes[key] + '"';
|
||||
str += ' ' + this.replace(key) + '="' + this.replace(this.attributes[key]) + '"';
|
||||
}
|
||||
}
|
||||
if (this.isEmpty()) {
|
||||
|
@ -101,7 +106,7 @@ export class Element {
|
|||
|
||||
content() {
|
||||
if (!this.isEmptyValue()) {
|
||||
return this.value;
|
||||
return this.replace(this.value);
|
||||
}
|
||||
|
||||
let str = '';
|
||||
|
@ -120,7 +125,7 @@ export class Element {
|
|||
if (this.isEmpty()) {
|
||||
return '\n';
|
||||
}
|
||||
let str = '</' + this.name + '>\n';
|
||||
let str = '</' + this.replace(this.name) + '>\n';
|
||||
if (!this.isEmptyValue()) {
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ export const BODY_TYPE = {
|
|||
export const ASSERTION_TYPE = {
|
||||
TEXT: "Text",
|
||||
REGEX: "Regex",
|
||||
RESPONSE_TIME: "Response Time"
|
||||
DURATION: "Duration"
|
||||
}
|
||||
|
||||
export const ASSERTION_REGEX_SUBJECT = {
|
||||
|
@ -94,6 +94,7 @@ export class BaseConfig {
|
|||
export class Test extends BaseConfig {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.type = "MS API CONFIG";
|
||||
this.version = '1.0.0';
|
||||
this.id = uuid();
|
||||
this.name = undefined;
|
||||
|
@ -104,6 +105,16 @@ export class Test extends BaseConfig {
|
|||
this.sets({scenarioDefinition: Scenario}, options);
|
||||
}
|
||||
|
||||
export() {
|
||||
let obj = {
|
||||
type: this.type,
|
||||
version: this.version,
|
||||
scenarios: this.scenarioDefinition
|
||||
};
|
||||
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
|
||||
initOptions(options) {
|
||||
options = options || {};
|
||||
options.scenarioDefinition = options.scenarioDefinition || [new Scenario()];
|
||||
|
@ -253,7 +264,7 @@ export class Assertions extends BaseConfig {
|
|||
|
||||
initOptions(options) {
|
||||
options = options || {};
|
||||
options.duration = new ResponseTime(options.duration);
|
||||
options.duration = new Duration(options.duration);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
@ -291,9 +302,9 @@ export class Regex extends AssertionType {
|
|||
}
|
||||
}
|
||||
|
||||
export class ResponseTime extends AssertionType {
|
||||
export class Duration extends AssertionType {
|
||||
constructor(options) {
|
||||
super(ASSERTION_TYPE.RESPONSE_TIME);
|
||||
super(ASSERTION_TYPE.DURATION);
|
||||
this.value = undefined;
|
||||
|
||||
this.set(options);
|
||||
|
@ -377,8 +388,8 @@ class JMXRequest {
|
|||
if (request && request instanceof Request && request.url) {
|
||||
let url = new URL(request.url);
|
||||
this.method = request.method;
|
||||
this.hostname = url.hostname;
|
||||
this.pathname = url.pathname;
|
||||
this.hostname = decodeURIComponent(url.hostname);
|
||||
this.pathname = decodeURIComponent(url.pathname);
|
||||
this.port = url.port;
|
||||
this.protocol = url.protocol.split(":")[0];
|
||||
if (this.method.toUpperCase() !== "GET") {
|
||||
|
@ -418,7 +429,6 @@ class JMXGenerator {
|
|||
addScenarios(testPlan, scenarios) {
|
||||
scenarios.forEach(s => {
|
||||
let scenario = s.clone();
|
||||
scenario.name = this.replace(scenario.name);
|
||||
|
||||
let threadGroup = new ThreadGroup(scenario.name || "");
|
||||
|
||||
|
@ -429,8 +439,6 @@ class JMXGenerator {
|
|||
scenario.requests.forEach(request => {
|
||||
if (!request.isValid()) return;
|
||||
|
||||
request.name = this.replace(request.name);
|
||||
|
||||
let httpSamplerProxy = new HTTPSamplerProxy(request.name || "", new JMXRequest(request));
|
||||
|
||||
this.addRequestHeader(httpSamplerProxy, request);
|
||||
|
@ -453,7 +461,7 @@ class JMXGenerator {
|
|||
}
|
||||
|
||||
addScenarioVariables(threadGroup, scenario) {
|
||||
let args = this.replaceKV(scenario.variables);
|
||||
let args = this.filterKV(scenario.variables);
|
||||
if (args.length > 0) {
|
||||
let name = scenario.name + " Variables"
|
||||
threadGroup.put(new Arguments(name, args));
|
||||
|
@ -461,7 +469,7 @@ class JMXGenerator {
|
|||
}
|
||||
|
||||
addScenarioHeaders(threadGroup, scenario) {
|
||||
let headers = this.replaceKV(scenario.headers);
|
||||
let headers = this.filterKV(scenario.headers);
|
||||
if (headers.length > 0) {
|
||||
let name = scenario.name + " Headers"
|
||||
threadGroup.put(new HeaderManager(name, headers));
|
||||
|
@ -470,14 +478,14 @@ class JMXGenerator {
|
|||
|
||||
addRequestHeader(httpSamplerProxy, request) {
|
||||
let name = request.name + " Headers";
|
||||
let headers = this.replaceKV(request.headers);
|
||||
let headers = this.filterKV(request.headers);
|
||||
if (headers.length > 0) {
|
||||
httpSamplerProxy.put(new HeaderManager(name, headers));
|
||||
}
|
||||
}
|
||||
|
||||
addRequestArguments(httpSamplerProxy, request) {
|
||||
let args = this.replaceKV(request.parameters);
|
||||
let args = this.filterKV(request.parameters);
|
||||
if (args.length > 0) {
|
||||
httpSamplerProxy.add(new HTTPSamplerArguments(args));
|
||||
}
|
||||
|
@ -486,10 +494,10 @@ class JMXGenerator {
|
|||
addRequestBody(httpSamplerProxy, request) {
|
||||
let body = [];
|
||||
if (request.body.isKV()) {
|
||||
body = this.replaceKV(request.body.kvs);
|
||||
body = this.filterKV(request.body.kvs);
|
||||
} else {
|
||||
httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true);
|
||||
body.push({name: '', value: this.replace(request.body.raw), encode: false});
|
||||
body.push({name: '', value: request.body.raw, encode: false});
|
||||
}
|
||||
|
||||
httpSamplerProxy.add(new HTTPSamplerArguments(body));
|
||||
|
@ -510,9 +518,9 @@ class JMXGenerator {
|
|||
}
|
||||
|
||||
getAssertion(regex) {
|
||||
let name = this.replace(regex.description);
|
||||
let name = regex.description;
|
||||
let type = JMX_ASSERTION_CONDITION.CONTAINS; // 固定用Match,自己写正则
|
||||
let value = this.replace(regex.expression);
|
||||
let value = regex.expression;
|
||||
switch (regex.subject) {
|
||||
case ASSERTION_REGEX_SUBJECT.RESPONSE_CODE:
|
||||
return new ResponseCodeAssertion(name, type, value);
|
||||
|
@ -546,8 +554,8 @@ class JMXGenerator {
|
|||
|
||||
getExtractor(extractCommon) {
|
||||
let props = {
|
||||
name: this.replace(extractCommon.variable),
|
||||
expression: this.replace(extractCommon.expression),
|
||||
name: extractCommon.variable,
|
||||
expression: extractCommon.expression,
|
||||
}
|
||||
let testName = props.name
|
||||
switch (extractCommon.type) {
|
||||
|
@ -569,19 +577,8 @@ class JMXGenerator {
|
|||
return config.isValid();
|
||||
}
|
||||
|
||||
replace(str) {
|
||||
if (!str || !(typeof str === 'string')) return str;
|
||||
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'").replace(/"/g, """);
|
||||
}
|
||||
|
||||
replaceKV(kvs) {
|
||||
let results = [];
|
||||
kvs.filter(this.filter).forEach(kv => {
|
||||
let name = this.replace(kv.name);
|
||||
let value = this.replace(kv.value);
|
||||
results.push(new KeyValue(name, value));
|
||||
});
|
||||
return results;
|
||||
filterKV(kvs) {
|
||||
return kvs.filter(this.filter);
|
||||
}
|
||||
|
||||
toXML() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<router-link v-if="this.show" class="create-test" :to="this.to" v-permission="this.permission">
|
||||
<router-link class="create-test" :to="this.to" v-permission="this.permission">
|
||||
<el-button type="primary" size="small">{{this.title}}</el-button>
|
||||
</router-link>
|
||||
</template>
|
||||
|
@ -8,7 +8,6 @@
|
|||
export default {
|
||||
name: "MsCreateTest",
|
||||
props: {
|
||||
show: Boolean,
|
||||
to: [String, Object],
|
||||
title: {
|
||||
type: String,
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-row type="flex" justify="center">
|
||||
<ms-create-test v-permission="['test_manager','test_user']" :to="'/performance/test/create'"/>
|
||||
<ms-create-test :to="'/performance/test/create'"/>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="8"/>
|
||||
|
|
|
@ -3,17 +3,9 @@
|
|||
<ms-main-container>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<div>
|
||||
<el-row type="flex" justify="space-between" align="middle">
|
||||
<span class="title">{{$t('commons.test')}}</span>
|
||||
<span class="search">
|
||||
<el-input type="text" size="small" :placeholder="$t('load_test.search_by_name')"
|
||||
prefix-icon="el-icon-search"
|
||||
maxlength="60"
|
||||
v-model="condition.name" @change="search" clearable/>
|
||||
</span>
|
||||
</el-row>
|
||||
</div>
|
||||
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="search"
|
||||
:title="$t('commons.test')"
|
||||
@create="create" :createTip="$t('load_test.create')"/>
|
||||
</template>
|
||||
|
||||
<el-table :data="tableData" class="test-content"
|
||||
|
@ -89,9 +81,11 @@
|
|||
import MsPerformanceTestStatus from "./PerformanceTestStatus";
|
||||
import MsTableOperators from "../../common/components/MsTableOperators";
|
||||
import {_filter, _sort} from "../../../../common/js/utils";
|
||||
import MsTableHeader from "../../common/components/MsTableHeader";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MsTableHeader,
|
||||
MsPerformanceTestStatus,
|
||||
MsTablePagination,
|
||||
MsTableOperator,
|
||||
|
@ -210,6 +204,9 @@
|
|||
this.$router.push({
|
||||
path: '/performance/test/edit/' + row.id,
|
||||
})
|
||||
},
|
||||
create() {
|
||||
this.$router.push('/performance/test/create');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -304,6 +304,12 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!this.threadNumber || !this.duration || !this.rampUpTime || !this.step || !this.rpsLimit) {
|
||||
this.$warning(this.$t('load_test.pressure_config_params_is_empty'));
|
||||
this.$emit('changeActive', '1');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
convertProperty() {
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
</el-dialog>
|
||||
|
||||
<!--Change personal password-->
|
||||
<el-dialog :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="30%" left>
|
||||
<el-dialog :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="35%" left>
|
||||
<el-form :model="ruleForm" :rules="rules" ref="editPasswordForm" label-width="120px" class="demo-ruleForm">
|
||||
<el-form-item :label="$t('member.old_password')" prop="password" style="margin-bottom: 29px">
|
||||
<el-input v-model="ruleForm.password" autocomplete="off" show-password/>
|
||||
|
@ -128,12 +128,6 @@
|
|||
rules:{
|
||||
password: [
|
||||
{required: true, message: this.$t('user.input_password'), trigger: 'blur'},
|
||||
{
|
||||
required: true,
|
||||
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/,
|
||||
message: this.$t('member.password_format_is_incorrect'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
newpassword: [
|
||||
{required: true, message: this.$t('user.input_password'), trigger: 'blur'},
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<el-input v-model="form.name" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.description')" prop="description">
|
||||
<el-input v-model="form.description" autocomplete="off"/>
|
||||
<el-input v-model="form.description" autocomplete="off" type="textarea"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template v-slot:footer>
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<el-form-item :label="$t('commons.name')" prop="name">
|
||||
<el-input v-model="form.name" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.description')">
|
||||
<el-form-item :label="$t('commons.description')" prop="description">
|
||||
<el-input type="textarea" v-model="form.description"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('workspace.organization_name')" prop="organizationId">
|
||||
|
|
|
@ -64,8 +64,9 @@
|
|||
<el-form-item :label="$t('commons.phone')" prop="phone">
|
||||
<el-input v-model="form.phone" autocomplete="off" :placeholder="$t('user.input_phone')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.password')" prop="password">
|
||||
<el-input v-model="form.password" autocomplete="new-password" show-password :placeholder="$t('user.input_password')"/>
|
||||
<el-form-item :label="$t('commons.password')" prop="password" style="margin-bottom: 29px">
|
||||
<el-input v-model="form.password" autocomplete="new-password" show-password
|
||||
:placeholder="$t('user.input_password')"/>
|
||||
</el-form-item>
|
||||
<div v-for="(role, index) in form.roles" :key="index">
|
||||
<el-form-item :label="$t('commons.role')+index"
|
||||
|
|
|
@ -409,7 +409,7 @@
|
|||
},
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/list/all', {workspaceId:workspaceId}, response => {
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId:workspaceId}, response => {
|
||||
this.maintainerOptions = response.data;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -188,7 +188,7 @@
|
|||
},
|
||||
setPrincipalOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/list/all', {workspaceId:workspaceId}, response => {
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId:workspaceId}, response => {
|
||||
this.principalOptions = response.data;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -30,24 +30,23 @@
|
|||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<span @click.stop="clickt = 'stop'">
|
||||
<el-dropdown v-permission="['test_manager','test_user']" class="test-case-status" @command="statusChange">
|
||||
<el-dropdown class="test-case-status" @command="statusChange">
|
||||
<span class="el-dropdown-link">
|
||||
<plan-status-table-item :value="scope.row.status"/>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown" chang>
|
||||
<el-dropdown-item :command="{id: scope.row.id, status: 'Prepare'}">
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Prepare'}">
|
||||
{{$t('test_track.plan.plan_status_prepare')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{id: scope.row.id, status: 'Underway'}">
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Underway'}">
|
||||
{{$t('test_track.plan.plan_status_running')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{id: scope.row.id, status: 'Completed'}">
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Completed'}">
|
||||
{{$t('test_track.plan.plan_status_completed')}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
<plan-status-table-item v-permission="['test_viewer']" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
|
@ -115,7 +114,7 @@
|
|||
import MsTableOperator from "../../../common/components/MsTableOperator";
|
||||
import PlanStatusTableItem from "../../common/tableItems/plan/PlanStatusTableItem";
|
||||
import PlanStageTableItem from "../../common/tableItems/plan/PlanStageTableItem";
|
||||
import {_filter, _sort} from "../../../../../common/js/utils";
|
||||
import {_filter, _sort, checkoutTestManagerOrTestUser} from "../../../../../common/js/utils";
|
||||
import TestReportTemplateList from "../view/comonents/TestReportTemplateList";
|
||||
import TestCaseReportView from "../view/comonents/report/TestCaseReportView";
|
||||
import MsDeleteConfirm from "../../../common/components/MsDeleteConfirm";
|
||||
|
@ -137,6 +136,7 @@
|
|||
condition: {},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
isTestManagerOrTestUser: false,
|
||||
total: 0,
|
||||
tableData: [],
|
||||
statusFilters: [
|
||||
|
@ -160,6 +160,7 @@
|
|||
},
|
||||
created() {
|
||||
this.projectId = this.$route.params.projectId;
|
||||
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
|
||||
this.initTableData();
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
methods: {
|
||||
setMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/list/all', {workspaceId:workspaceId}, response => {
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId:workspaceId}, response => {
|
||||
this.executorOptions = response.data;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -289,9 +289,21 @@
|
|||
param.issues = JSON.stringify(this.testCase.issues);
|
||||
this.$post('/test/plan/case/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.updateTestCases(param);
|
||||
this.setPlanStatus(this.testCase.planId);
|
||||
});
|
||||
},
|
||||
updateTestCases(param) {
|
||||
for (let i = 0; i < this.testCases.length; i++) {
|
||||
let testCase = this.testCases[i];
|
||||
if (testCase.id === param.id) {
|
||||
testCase.results = param.results;
|
||||
testCase.issues = param.issues;
|
||||
testCase.status = param.status;
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
handleNext() {
|
||||
this.index++;
|
||||
this.getTestCase(this.index);
|
||||
|
|
|
@ -89,27 +89,26 @@
|
|||
:label="$t('test_track.plan_view.execute_result')">
|
||||
<template v-slot:default="scope">
|
||||
<span @click.stop="clickt = 'stop'">
|
||||
<el-dropdown v-permission="['test_manager','test_user']" class="test-case-status" @command="statusChange" >
|
||||
<el-dropdown class="test-case-status" @command="statusChange" >
|
||||
<span class="el-dropdown-link">
|
||||
<status-table-item :value="scope.row.status"/>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown" chang>
|
||||
<el-dropdown-item :command="{id: scope.row.id, status: 'Pass'}">
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Pass'}">
|
||||
{{$t('test_track.plan_view.pass')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{id: scope.row.id, status: 'Failure'}">
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Failure'}">
|
||||
{{$t('test_track.plan_view.failure')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{id: scope.row.id, status: 'Blocking'}">
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Blocking'}">
|
||||
{{$t('test_track.plan_view.blocking')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{id: scope.row.id, status: 'Skip'}">
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Skip'}">
|
||||
{{$t('test_track.plan_view.skip')}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
<status-table-item v-permission="['test_viewer']" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
@ -199,6 +198,7 @@
|
|||
selectIds: new Set(),
|
||||
testPlan: {},
|
||||
isReadOnly: false,
|
||||
isTestManagerOrTestUser: false,
|
||||
priorityFilters: [
|
||||
{text: 'P0', value: 'P0'},
|
||||
{text: 'P1', value: 'P1'},
|
||||
|
@ -244,6 +244,7 @@
|
|||
},
|
||||
mounted() {
|
||||
this.refreshTableAndPlan();
|
||||
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
|
||||
},
|
||||
methods: {
|
||||
initTableData() {
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
|
||||
<common-component :title="$t('test_track.plan_view.failure_case')">
|
||||
<template>
|
||||
<el-table
|
||||
row-key="id"
|
||||
:data="failureTestCases">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('commons.name')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="priority"
|
||||
column-key="priority"
|
||||
:label="$t('test_track.case.priority')">
|
||||
<template v-slot:default="scope">
|
||||
<priority-table-item :value="scope.row.priority" ref="priority"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="type"
|
||||
column-key="type"
|
||||
:label="$t('test_track.case.type')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<type-table-item :value="scope.row.type"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="method"
|
||||
column-key="method"
|
||||
:label="$t('test_track.case.method')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<method-table-item :value="scope.row.method"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="nodePath"
|
||||
:label="$t('test_track.case.module')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="executorName"
|
||||
:label="$t('test_track.plan_view.executor')">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="status"
|
||||
column-key="status"
|
||||
:label="$t('test_track.plan_view.execute_result')">
|
||||
<template v-slot:default="scope">
|
||||
<status-table-item :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="updateTime"
|
||||
:label="$t('commons.update_time')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
</common-component>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommonComponent from "./CommonComponent";
|
||||
import PriorityTableItem from "../../../../../common/tableItems/planview/PriorityTableItem";
|
||||
import TypeTableItem from "../../../../../common/tableItems/planview/TypeTableItem";
|
||||
import MethodTableItem from "../../../../../common/tableItems/planview/MethodTableItem";
|
||||
import StatusTableItem from "../../../../../common/tableItems/planview/StatusTableItem";
|
||||
export default {
|
||||
name: "FailureResultComponent",
|
||||
components: {StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem, CommonComponent},
|
||||
props: {
|
||||
failureTestCases: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [
|
||||
{
|
||||
name: 'testCase1',
|
||||
priority: 'P1',
|
||||
type: 'api',
|
||||
method: 'auto',
|
||||
nodePath: '/module1/module2',
|
||||
executorName: "Tom",
|
||||
status: "Failure",
|
||||
updateTime: new Date(),
|
||||
},
|
||||
{
|
||||
name: 'testCase2',
|
||||
priority: 'P0',
|
||||
type: 'functional',
|
||||
method: 'manual',
|
||||
nodePath: '/module1',
|
||||
executorName: "Micheal",
|
||||
status: "Failure",
|
||||
updateTime: new Date(),
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -6,6 +6,7 @@
|
|||
<base-info-component :is-report="false" v-if="preview.id == 1"/>
|
||||
<test-result-component v-if="preview.id == 2"/>
|
||||
<test-result-chart-component v-if="preview.id == 3"/>
|
||||
<failure-result-component v-if="preview.id == 4"/>
|
||||
<rich-text-component :preview="preview" v-if="preview.type != 'system'"/>
|
||||
</div>
|
||||
|
||||
|
@ -14,6 +15,7 @@
|
|||
<base-info-component :report-info="metric" v-if="preview.id == 1"/>
|
||||
<test-result-component :test-results="metric.moduleExecuteResult" v-if="preview.id == 2"/>
|
||||
<test-result-chart-component :execute-result="metric.executeResult" v-if="preview.id == 3"/>
|
||||
<failure-result-component :failure-test-cases="metric.failureTestCases" v-if="preview.id == 4"/>
|
||||
<rich-text-component :is-report-view="isReportView" :preview="preview" v-if="preview.type != 'system'"/>
|
||||
</div>
|
||||
|
||||
|
@ -25,9 +27,12 @@
|
|||
import TestResultComponent from "./TestResultComponent";
|
||||
import TestResultChartComponent from "./TestResultChartComponent";
|
||||
import RichTextComponent from "./RichTextComponent";
|
||||
import FailureResultComponent from "./FailureResultComponent";
|
||||
export default {
|
||||
name: "TemplateComponent",
|
||||
components: {RichTextComponent, TestResultChartComponent, TestResultComponent, BaseInfoComponent},
|
||||
components: {
|
||||
FailureResultComponent,
|
||||
RichTextComponent, TestResultChartComponent, TestResultComponent, BaseInfoComponent},
|
||||
props: {
|
||||
preview: {
|
||||
type: Object
|
||||
|
|
|
@ -76,10 +76,11 @@
|
|||
[1, { name: this.$t('test_track.plan_view.base_info'), id: 1 , type: 'system'}],
|
||||
[2, { name: this.$t('test_track.plan_view.test_result'), id: 2 , type: 'system'}],
|
||||
[3, { name: this.$t('test_track.plan_view.result_distribution'), id: 3 ,type: 'system'}],
|
||||
[4, { name: this.$t('test_track.plan_view.custom_component'), id: 4 ,type: 'custom'}]
|
||||
[4, { name: this.$t('test_track.plan_view.failure_case'), id: 4 ,type: 'system'}],
|
||||
[5, { name: this.$t('test_track.plan_view.custom_component'), id: 5 ,type: 'custom'}]
|
||||
]
|
||||
),
|
||||
components: [4],
|
||||
components: [5],
|
||||
previews: [],
|
||||
template: {},
|
||||
isReport: false
|
||||
|
@ -108,12 +109,12 @@
|
|||
this.template = {
|
||||
name: '',
|
||||
content: {
|
||||
components: [1,2,3,4],
|
||||
components: [1,2,3,4,5],
|
||||
customComponent: new Map()
|
||||
}
|
||||
};
|
||||
this.previews = [];
|
||||
this.components = [4];
|
||||
this.components = [5];
|
||||
if (id) {
|
||||
this.type = 'edit';
|
||||
this.getTemplateById(id);
|
||||
|
|
|
@ -68,7 +68,8 @@
|
|||
[1, { name: this.$t('test_track.plan_view.base_info'), id: 1 , type: 'system'}],
|
||||
[2, { name: this.$t('test_track.plan_view.test_result'), id: 2 , type: 'system'}],
|
||||
[3, { name: this.$t('test_track.plan_view.result_distribution'), id: 3 ,type: 'system'}],
|
||||
[4, { name: this.$t('test_track.plan_view.custom_component'), id: 4 ,type: 'custom'}]
|
||||
[4, { name: this.$t('test_track.plan_view.failure_case'), id: 4 ,type: 'system'}],
|
||||
[5, { name: this.$t('test_track.plan_view.custom_component'), id: 5 ,type: 'custom'}]
|
||||
]
|
||||
),
|
||||
isTestManagerOrTestUser: false
|
||||
|
@ -165,6 +166,15 @@
|
|||
getMetric() {
|
||||
this.result = this.$get('/test/plan/get/metric/' + this.planId, response => {
|
||||
this.metric = response.data;
|
||||
if (!this.metric.failureTestCases) {
|
||||
this.metric.failureTestCases = [];
|
||||
}
|
||||
if (!this.metric.executeResult) {
|
||||
this.metric.executeResult = [];
|
||||
}
|
||||
if (!this.metric.moduleExecuteResult) {
|
||||
this.metric.moduleExecuteResult = [];
|
||||
}
|
||||
if (this.report.startTime) {
|
||||
this.metric.startTime = new Date(this.report.startTime);
|
||||
}
|
||||
|
|
|
@ -126,6 +126,7 @@ export function _filter(filters, condition) {
|
|||
condition.filters = {};
|
||||
}
|
||||
for (let filter in filters) {
|
||||
if (filters.hasOwnProperty(filter)) {
|
||||
if (filters[filter] && filters[filter].length > 0) {
|
||||
condition.filters[filter] = filters[filter];
|
||||
} else {
|
||||
|
@ -133,11 +134,12 @@ export function _filter(filters, condition) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//表格数据排序
|
||||
export function _sort(column, condition) {
|
||||
column.prop = humpToLine(column.prop);
|
||||
if (column.order == 'descending') {
|
||||
if (column.order === 'descending') {
|
||||
column.order = 'desc';
|
||||
} else {
|
||||
column.order = 'asc';
|
||||
|
@ -147,13 +149,28 @@ export function _sort(column, condition) {
|
|||
}
|
||||
let hasProp = false;
|
||||
condition.orders.forEach(order => {
|
||||
if (order.name == column.prop) {
|
||||
if (order.name === column.prop) {
|
||||
order.type = column.order;
|
||||
hasProp = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (!hasProp) {
|
||||
condition.orders.push({name: column.prop, type: column.order});
|
||||
}
|
||||
}
|
||||
|
||||
export function downloadFile(name, content) {
|
||||
const blob = new Blob([content]);
|
||||
if ("download" in document.createElement("a")) {
|
||||
// 非IE下载
|
||||
// chrome/firefox
|
||||
let aTag = document.createElement('a');
|
||||
aTag.download = name;
|
||||
aTag.href = URL.createObjectURL(blob);
|
||||
aTag.click();
|
||||
URL.revokeObjectURL(aTag.href)
|
||||
} else {
|
||||
// IE10+下载
|
||||
navigator.msSaveBlob(blob, name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ export default {
|
|||
'special_characters_are_not_supported': 'Special characters are not supported',
|
||||
'mobile_number_format_is_incorrect': 'Mobile number format is incorrect',
|
||||
'email_format_is_incorrect': 'Email format is incorrect',
|
||||
'password_format_is_incorrect': 'Password format is incorrect (At least 8-16 characters, at least 1 uppercase letter, 1 lowercase letter and 1 number)',
|
||||
'password_format_is_incorrect': 'Valid password: 8-16 digits, English upper and lower case letters + numbers ',
|
||||
'old_password': 'Old Password',
|
||||
'new_password': 'New Password',
|
||||
'remove_member': 'Are you sure you want to remove this member',
|
||||
|
@ -268,6 +268,7 @@ export default {
|
|||
'download_log_file': 'Download',
|
||||
'user_name': 'Creator',
|
||||
'special_characters_are_not_supported': 'Test name does not support special characters',
|
||||
'pressure_config_params_is_empty': 'Pressure configuration parameters cannot be empty!'
|
||||
},
|
||||
api_test: {
|
||||
creator: "Creator",
|
||||
|
@ -283,6 +284,7 @@ export default {
|
|||
key: "Key",
|
||||
value: "Value",
|
||||
create_performance_test: "Create Performance Test",
|
||||
export_config: "Export Configuration",
|
||||
scenario: {
|
||||
config: "Scenario Config",
|
||||
input_name: "Please enter the scenario name",
|
||||
|
@ -517,6 +519,7 @@ export default {
|
|||
create_template: "Create template",
|
||||
report_template: "Report template",
|
||||
test_detail: "Test detail",
|
||||
failure_case: "Failure case",
|
||||
}
|
||||
},
|
||||
test_resource_pool: {
|
||||
|
|
|
@ -154,7 +154,7 @@ export default {
|
|||
'special_characters_are_not_supported': '不支持特殊字符',
|
||||
'mobile_number_format_is_incorrect': '手机号码格式不正确',
|
||||
'email_format_is_incorrect': '邮箱格式不正确',
|
||||
'password_format_is_incorrect': '密码格式不正确(至少8-16个字符,至少1个大写字母,1个小写字母和1个数字)',
|
||||
'password_format_is_incorrect': '有效密码:8-16位,英文大小写字母+数字',
|
||||
'old_password': '旧密码',
|
||||
'new_password': '新密码',
|
||||
'remove_member': '确定要移除该成员吗',
|
||||
|
@ -268,6 +268,7 @@ export default {
|
|||
'pressure_prediction_chart': '压力预估图',
|
||||
'user_name': '创建人',
|
||||
'special_characters_are_not_supported': '测试名称不支持特殊字符',
|
||||
'pressure_config_params_is_empty': '压力配置参数不能为空!'
|
||||
},
|
||||
api_test: {
|
||||
creator: "创建人",
|
||||
|
@ -282,6 +283,7 @@ export default {
|
|||
key: "键",
|
||||
value: "值",
|
||||
create_performance_test: "创建性能测试",
|
||||
export_config: "导出配置",
|
||||
scenario: {
|
||||
config: "场景配置",
|
||||
input_name: "请输入场景名称",
|
||||
|
@ -516,6 +518,7 @@ export default {
|
|||
create_template: "新建模版",
|
||||
report_template: "测试报告模版",
|
||||
test_detail: "测试详情",
|
||||
failure_case: "失败用例",
|
||||
}
|
||||
},
|
||||
test_resource_pool: {
|
||||
|
|
|
@ -152,7 +152,7 @@ export default {
|
|||
'special_characters_are_not_supported': '不支持特殊字符',
|
||||
'mobile_number_format_is_incorrect': '手機號碼格式不正確',
|
||||
'email_format_is_incorrect': '郵箱格式不正確',
|
||||
'password_format_is_incorrect': '密碼格式不正確(至少8-16個字符,至少1個大寫字母,1個小寫字母和1個數字)',
|
||||
'password_format_is_incorrect': '有效密碼:8-16比特,英文大小寫字母+數位',
|
||||
'old_password': '舊密碼',
|
||||
'new_password': '新密碼',
|
||||
'remove_member': '確定要移除該成員嗎',
|
||||
|
@ -266,6 +266,7 @@ export default {
|
|||
'pressure_prediction_chart': '壓力預估圖',
|
||||
'user_name': '創建人',
|
||||
'special_characters_are_not_supported': '測試名稱不支持特殊字符',
|
||||
'pressure_config_params_is_empty': '壓力配置參數不能為空!'
|
||||
},
|
||||
api_test: {
|
||||
title: "測試",
|
||||
|
@ -280,6 +281,7 @@ export default {
|
|||
key: "鍵",
|
||||
value: "值",
|
||||
create_performance_test: "創建性能測試",
|
||||
export_config: "匯出配寘",
|
||||
scenario: {
|
||||
creator: "創建人",
|
||||
config: "場景配寘",
|
||||
|
@ -515,6 +517,7 @@ export default {
|
|||
create_template: "新建模版",
|
||||
report_template: "測試報告模版",
|
||||
test_detail: "測試詳情",
|
||||
failure_case: "失敗用例",
|
||||
}
|
||||
},
|
||||
test_resource_pool: {
|
||||
|
|
Loading…
Reference in New Issue