This commit is contained in:
liqiang-fit2cloud 2022-12-06 17:15:09 +08:00
commit b66d5e1ca4
51 changed files with 596 additions and 288 deletions

View File

@ -230,7 +230,7 @@ public class MsScenario extends MsTestElement {
orgJSONArray.forEach(obj -> {
JSONObject orgJsonObject = (JSONObject) obj;
hashTree.forEach(targetObj -> {
if (StringUtils.equals(orgJsonObject.optString(MsHashTreeService.ID), targetObj.getId())) {
if (StringUtils.equals(orgJsonObject.optString(MsHashTreeService.RESOURCE_ID), targetObj.getResourceId())) {
setRefEnable(targetObj, orgJsonObject);
}
});

View File

@ -3,10 +3,8 @@ package io.metersphere.api.exec.generator;
import com.google.gson.*;
import io.metersphere.commons.constants.PropertyConstant;
import io.metersphere.commons.utils.EnumPropertyUtil;
import io.metersphere.commons.utils.JSONUtil;
import io.metersphere.jmeter.utils.ScriptEngineUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
@ -14,7 +12,6 @@ import org.json.JSONObject;
import org.springframework.util.NumberUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@ -252,12 +249,6 @@ public class JSONSchemaRunTest {
if (isMock(object)) {
String value = ScriptEngineUtils.buildFunctionCallString(object.get(PropertyConstant.MOCK).getAsJsonObject().get(PropertyConstant.MOCK).getAsString());
return value;
} else if (object.has(PropertyConstant.ENUM)) {
List<Object> list = EnumPropertyUtil.analyzeEnumProperty(object);
if (CollectionUtils.isNotEmpty(list)) {
int index = (int) (Math.random() * list.size());
return list.get(index);
}
}
} catch (Exception e) {
return object.get(PropertyConstant.MOCK).getAsJsonObject().get(PropertyConstant.MOCK);

View File

@ -2,7 +2,6 @@ package io.metersphere.api.exec.queue;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.JMeterThreadUtils;
import io.metersphere.cache.JMeterEngineCache;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.utils.LoggerUtil;
@ -24,9 +23,6 @@ public class ExecTask implements Runnable {
Object res = PoolExecBlockingQueueUtil.take(request.getReportId());
if (res == null && !JMeterThreadUtils.isRunning(request.getReportId(), request.getTestId())) {
LoggerUtil.info("任务执行超时", request.getReportId());
if (JMeterEngineCache.runningEngine.containsKey(request.getReportId())) {
JMeterEngineCache.runningEngine.remove(request.getReportId());
}
}
}
}

View File

@ -3,7 +3,6 @@ package io.metersphere.api.jmeter;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.api.jmeter.utils.ReportStatusUtil;
import io.metersphere.cache.JMeterEngineCache;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.FileUtils;
@ -121,9 +120,6 @@ public class MsApiBackendListener extends AbstractBackendListenerClient implemen
LoggerUtil.info("进入监听开始关闭CSV", dto.getReportId());
FileServer.getFileServer().closeCsv(dto.getReportId());
}
if (JMeterEngineCache.runningEngine.containsKey(dto.getReportId())) {
JMeterEngineCache.runningEngine.remove(dto.getReportId());
}
}
}

View File

@ -16,7 +16,6 @@ import io.metersphere.base.mapper.ApiScenarioReportMapper;
import io.metersphere.base.mapper.ApiTestCaseMapper;
import io.metersphere.base.mapper.plan.TestPlanApiCaseMapper;
import io.metersphere.base.mapper.plan.TestPlanApiScenarioMapper;
import io.metersphere.cache.JMeterEngineCache;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.utils.BeanUtils;
@ -153,9 +152,6 @@ public class RemakeReportService {
dto.setQueueId(request.getQueueId());
dto.setTestId(request.getTestId());
if (JMeterEngineCache.runningEngine.containsKey(dto.getReportId())) {
JMeterEngineCache.runningEngine.remove(dto.getReportId());
}
LoggerUtil.info("进入异常结果处理:" + dto.getRunMode() + " 整体处理完成", dto.getReportId());
// 全局并发队列
PoolExecBlockingQueueUtil.offer(dto.getReportId());

View File

@ -1064,21 +1064,24 @@ public class ApiTestCaseService {
}
try {
List<String> envIds = environments.stream().filter(t -> StringUtils.isNotBlank(t.getValue()) && !StringUtils.equalsIgnoreCase(t.getValue(), "null")).map(ParamsDTO::getValue).collect(Collectors.toList());
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
example.createCriteria().andIdIn(envIds);
List<ApiTestEnvironment> environmentList = apiTestEnvironmentMapper.selectByExample(example);
if (CollectionUtils.isEmpty(environmentList)) {
return null;
}
Map<String, String> envMap = environmentList.stream().collect(Collectors.toMap(ApiTestEnvironment::getId, ApiTestEnvironment::getName));
if (CollectionUtils.isNotEmpty(envIds)) {
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
example.createCriteria().andIdIn(envIds);
List<ApiTestEnvironment> environmentList = apiTestEnvironmentMapper.selectByExample(example);
Map<String, String> caseEnvMap = environments.stream().filter(t -> StringUtils.isNotBlank(t.getValue()) && !StringUtils.equalsIgnoreCase(t.getValue(), "null")).collect(HashMap::new, (m, v) -> m.put(v.getId(), v.getValue()), HashMap::putAll);
caseEnvMap.forEach((k, v) -> {
if (envMap.containsKey(v)) {
caseEnvMap.put(k, envMap.get(v));
if (CollectionUtils.isEmpty(environmentList)) {
return null;
}
});
return caseEnvMap;
Map<String, String> envMap = environmentList.stream().collect(Collectors.toMap(ApiTestEnvironment::getId, ApiTestEnvironment::getName));
Map<String, String> caseEnvMap = environments.stream().filter(t -> StringUtils.isNotBlank(t.getValue()) && !StringUtils.equalsIgnoreCase(t.getValue(), "null")).collect(HashMap::new, (m, v) -> m.put(v.getId(), v.getValue()), HashMap::putAll);
caseEnvMap.forEach((k, v) -> {
if (envMap.containsKey(v)) {
caseEnvMap.put(k, envMap.get(v));
}
});
return caseEnvMap;
}
} catch (Exception e) {
LogUtil.error("api case environmentId incorrect parsing", e);
}

View File

@ -4,21 +4,20 @@ import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.JMeterThreadUtils;
import io.metersphere.service.ApiExecutionQueueService;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiScenarioReportMapper;
import io.metersphere.base.mapper.TestResourceMapper;
import io.metersphere.base.mapper.TestResourcePoolMapper;
import io.metersphere.base.mapper.ext.BaseTaskMapper;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper;
import io.metersphere.base.mapper.ext.BaseTaskMapper;
import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.utils.JSON;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.NodeDTO;
import io.metersphere.jmeter.LocalRunner;
import io.metersphere.service.ApiExecutionQueueService;
import io.metersphere.task.dto.TaskCenterDTO;
import io.metersphere.task.dto.TaskCenterRequest;
import io.metersphere.task.dto.TaskRequestDTO;
@ -213,7 +212,6 @@ public class ExtApiTaskService extends TaskService {
}});
}
} else {
new LocalRunner().stop(reportId);
JMeterThreadUtils.stop(reportId);
}
}

View File

@ -695,6 +695,7 @@ export default {
this.reportExportVisible = true;
let reset = this.exportReportReset;
let name = this.report.name;
name = this.encodeSearchKey(name);
this.$nextTick(() => {
setTimeout(() => {
downloadPDF(document.getElementById('apiTestReport'), name || 'scenario-report');
@ -702,6 +703,38 @@ export default {
}, 5000);
});
},
//
encodeSearchKey(key) {
const encodeArr = [
{
code: '%',
encode: '%25',
},
{
code: '?',
encode: '%3F',
},
{
code: '#',
encode: '%23',
},
{
code: '&',
encode: '%26',
},
{
code: '=',
encode: '%3D',
},
];
return key.replace(/[%?#&=]/g, ($, index, str) => {
for (const k of encodeArr) {
if (k.code === $) {
return k.encode;
}
}
});
},
handleSave() {
if (!this.report.name) {
this.$warning(this.$t('api_test.automation.report_name_info'));

View File

@ -328,7 +328,7 @@ export default {
debugCode(data) {
if (data && this.node && this.node.data) {
if (data.error > 0) {
this.node.data.code = 'error';
this.node.data.code = 'Error';
} else {
this.node.data.code = this.node.data.code !== 'ERROR' ? 'Success' : 'Error';
}

View File

@ -133,9 +133,9 @@ export default {
getCode() {
if (this.node && this.node.data.code && this.node.data.debug) {
if (this.node.data.code && this.node.data.code === 'ERROR') {
return 'error';
return 'Error';
} else {
return 'success';
return 'Success';
}
}
return '';

View File

@ -8,6 +8,7 @@
type="primary"
size="mini"
style="margin: 10px 10px 0px"
:disabled="isReadOnly"
@click="openOneClickOperation">
{{ this.$t('commons.import') }}
</el-button>
@ -24,7 +25,7 @@
<json-schema-editor
v-if="reloadedApiVariable"
class="schema"
:disabled="jsonSchemaDisable"
:disabled="jsonSchemaDisable || isReadOnly"
:value="schema"
:show-mock-vars="showMockVars"
:scenario-definition="scenarioDefinition"
@ -82,6 +83,10 @@ export default {
return true;
},
},
isReadOnly: {
type: Boolean,
default: false,
},
},
created() {
if (!this.body.jsonSchema && this.body.raw && this.checkIsJson(this.body.raw)) {

View File

@ -81,6 +81,7 @@
size="small"
maxlength="200"
:placeholder="$t('commons.description')"
:disabled="isReadOnly"
show-word-limit>
</el-input>
</el-col>

View File

@ -47,13 +47,19 @@
</div>
<div v-if="body.type == 'JSON'">
<div style="padding: 10px">
<el-switch active-text="JSON-SCHEMA" v-model="body.format" @change="formatChange" active-value="JSON-SCHEMA" />
<el-switch
active-text="JSON-SCHEMA"
v-model="body.format"
@change="formatChange"
active-value="JSON-SCHEMA"
:disabled="isReadOnly" />
</div>
<ms-json-code-edit
v-if="body.format === 'JSON-SCHEMA'"
:body="body"
:scenario-definition="scenarioDefinition"
@editScenarioAdvance="editScenarioAdvance"
:is-read-only="isReadOnly"
ref="jsonCodeEdit" />
<ms-code-edit
v-else-if="codeEditActive"

View File

@ -21,6 +21,7 @@ public class SessionFilter implements WebFilter {
private static final String[] TO_SUB_SERVICE = new String[]{"/license", "/system", "/resource", "/sso/callback/logout", "/sso/callback/cas/logout"};
private static final String PERFORMANCE_DOWNLOAD_PREFIX = "/jmeter/";
private static final String API_DOWNLOAD_PREFIX = "/api/jmeter/";
private static final String TRACK_IMAGE_PREFIX = "/resource/md/get/url";
@Resource
private DiscoveryClient discoveryClient;
@ -36,15 +37,15 @@ public class SessionFilter implements WebFilter {
if (path.startsWith("/css") || path.startsWith("/js")) {
for (String prefix : PREFIX) {
if (path.contains(prefix)) {
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
String newPath = prefix + path;
ServerHttpRequest request = req.mutate().path(newPath).build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
return chain.filter(exchange.mutate().request(request).build());
return addPrefix(prefix, exchange, chain);
}
}
}
if (path.startsWith(TRACK_IMAGE_PREFIX)) {
return addPrefix("/track", exchange, chain);
}
// 有些url直接转到 sub-service
for (String prefix : TO_SUB_SERVICE) {
if (path.startsWith(prefix)) {
@ -53,31 +54,29 @@ public class SessionFilter implements WebFilter {
break;
}
String service = svc.get();
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
String newPath = "/" + service + "/" + path;
ServerHttpRequest request = req.mutate().path(newPath).build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
return chain.filter(exchange.mutate().request(request).build());
return addPrefix("/" + service + "/", exchange, chain);
}
}
// 从当前站点下载资源
if (path.startsWith(PERFORMANCE_DOWNLOAD_PREFIX)) {
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
String newPath = "/performance" + path;
ServerHttpRequest request = req.mutate().path(newPath).build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
return chain.filter(exchange.mutate().request(request).build());
return addPrefix("/performance", exchange, chain);
}
if (path.startsWith(API_DOWNLOAD_PREFIX)) {
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
String newPath = "/api" + path;
ServerHttpRequest request = req.mutate().path(newPath).build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
return chain.filter(exchange.mutate().request(request).build());
return addPrefix("/api", exchange, chain);
}
return chain.filter(exchange);
}
private Mono<Void> addPrefix(String prefix, final ServerWebExchange exchange, final WebFilterChain chain) {
ServerHttpRequest req = exchange.getRequest();
String path = req.getURI().getRawPath();
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
String newPath = prefix + path;
ServerHttpRequest request = req.mutate().path(newPath).build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
return chain.filter(exchange.mutate().request(request).build());
}
}

View File

@ -43,7 +43,10 @@ import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@ -243,6 +246,8 @@ public class SSOService {
RestTemplate restTemplate = getRestTemplateIgnoreSSL();
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
String credentials = EncryptUtils.base64Encoding(config.get("clientId") + ":" + config.get("secret"));
headers.add(HttpHeaders.AUTHORIZATION, "Basic " + credentials);
HttpEntity<String> param = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, param, String.class);
String content = response.getBody();
@ -263,7 +268,7 @@ public class SSOService {
private Optional<SessionUser> doOauth2Login(AuthSource authSource, String accessToken, WebSession session, Locale locale) throws Exception {
Map<String, String> oauth2Config = null;
Map<String, String> resultObj = null;
Map<String, Object> resultObj = null;
try {
oauth2Config = JSON.parseObject(authSource.getConfiguration(), new TypeReference<HashMap<String, String>>() {});
String userInfoUrl = oauth2Config.get("userInfoUrl");
@ -272,7 +277,7 @@ public class SSOService {
RestTemplate restTemplate = getRestTemplateIgnoreSSL();
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(userInfoUrl, HttpMethod.GET, httpEntity, String.class);
resultObj = JSON.parseObject(response.getBody(), new TypeReference<HashMap<String, String>>() {});
resultObj = JSON.parseObject(response.getBody(), new TypeReference<HashMap<String, Object>>() {});
} catch (Exception e) {
LogUtil.error("fail to get user info", e);
MSException.throwException("fail to get user info!");
@ -281,9 +286,9 @@ public class SSOService {
String attrMapping = oauth2Config.get("mapping");
Map<String, String> mapping = this.getOauth2AttrMapping(attrMapping);
String userid = resultObj.get(mapping.get("userid"));
String username = resultObj.get(mapping.get("username"));
String email = resultObj.get(mapping.get("email"));
String userid = (String) resultObj.get(mapping.get("userid"));
String username = (String) resultObj.get(mapping.get("username"));
String email = (String) resultObj.get(mapping.get("email"));
if (StringUtils.isBlank(userid)) {
MSException.throwException("userid is empty!");

View File

@ -281,6 +281,8 @@ export default {
.ms-right-fixed {
flex: 0;
margin-left: 0px;
height: calc(100vh);
background-color: #F5F6F7;
}
.ms-header-w {

View File

@ -329,10 +329,12 @@ export default {
if (source.type === 'OAuth2') {
url = config.authUrl
+ "?client_id=" + config.clientId
+ "&scope=" + config.scope
+ "&response_type=code"
+ "&redirect_uri=" + redirectUrl
+ "&state=" + authId;
if (config.scope) {
url += "&scope=" + config.scope;
}
}
if (url) {
window.location.href = url;

View File

@ -1,5 +1,5 @@
export default {
sync_add_api_load: 'Synchronously add associated api and load tests',
sync_add_api_load: 'Synchronously add associated test case',
next: 'Next',
total_size: 'Total {0}',
related_requirements: 'Related requirements',

View File

@ -1,5 +1,5 @@
export default {
sync_add_api_load: '同步添加关联的接口和性能测试',
sync_add_api_load: '同步添加关联测试的用例',
next: '下一条',
total_size: '共 {0} 条',
related_requirements: '关联需求',

View File

@ -1,5 +1,5 @@
export default {
sync_add_api_load: '同步添加關聯的接口和性能測試',
sync_add_api_load: '同步添加關聯測試的用例',
next: '下一條',
total_size: '共 {0} 條',
related_requirements: '關聯需求',

View File

@ -2027,7 +2027,8 @@ const message = {
api: "接口用例",
scene: "场景用例",
load: "性能用例",
functional: "功能用例"
functional: "功能用例",
ui: "UI用例",
}
}
},

View File

@ -1,13 +0,0 @@
package io.metersphere.cache;
import org.apache.jmeter.engine.StandardJMeterEngine;
import java.util.concurrent.ConcurrentHashMap;
public class JMeterEngineCache {
/**
* 执行中的线程池
*/
public static ConcurrentHashMap<String, StandardJMeterEngine> runningEngine = new ConcurrentHashMap<>();
}

View File

@ -1,15 +1,10 @@
package io.metersphere.jmeter;
import io.metersphere.cache.JMeterEngineCache;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.engine.JMeterEngineException;
import org.apache.jmeter.engine.StandardJMeterEngine;
import org.apache.jorphan.collections.HashTree;
import java.util.List;
public class LocalRunner {
private HashTree jmxTree;
@ -26,31 +21,8 @@ public class LocalRunner {
try {
LoggerUtil.info("LocalRunner 开始执行报告",report);
engine.runTest();
JMeterEngineCache.runningEngine.put(report, engine);
} catch (JMeterEngineException e) {
engine.stopTest(true);
}
}
public void stop(List<String> reports) {
if (CollectionUtils.isNotEmpty(reports)) {
for (String report : reports) {
StandardJMeterEngine engine = JMeterEngineCache.runningEngine.get(report);
if (engine != null) {
engine.stopTest();
JMeterEngineCache.runningEngine.remove(report);
}
}
}
}
public void stop(String report) {
if (StringUtils.isNotEmpty(report)) {
StandardJMeterEngine engine = JMeterEngineCache.runningEngine.get(report);
if (engine != null) {
engine.stopTest();
JMeterEngineCache.runningEngine.remove(report);
}
}
}
}

View File

@ -1,63 +0,0 @@
package io.metersphere.service.utils;
import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class MsClassLoader {
public final static ConcurrentHashMap<String, MsURLClassLoader> LOADER_CACHE = new ConcurrentHashMap<>();
public MsURLClassLoader loadJar(String path) {
try {
String jarName = path.substring(path.indexOf("_") + 1);
if (StringUtils.isNotEmpty(jarName) && jarName.endsWith(".jar")) {
jarName = jarName.substring(0, jarName.length() - 4);
}
MsURLClassLoader urlClassLoader = LOADER_CACHE.get(jarName);
if (urlClassLoader != null) {
return urlClassLoader;
}
urlClassLoader = new MsURLClassLoader();
File jarFile = new File(path);
URL jarUrl = jarFile.toURI().toURL();
urlClassLoader.addURLFile(jarUrl);
LOADER_CACHE.put(jarName, urlClassLoader);
return urlClassLoader;
} catch (Exception ex) {
LogUtil.error("加载JAR包失败" + ex.getMessage());
}
return null;
}
public Class loadClass(String jarName, String name) throws ClassNotFoundException {
if (StringUtils.isNotEmpty(jarName) && jarName.endsWith(".jar")) {
jarName = jarName.substring(0, jarName.length() - 4);
}
MsURLClassLoader urlClassLoader = LOADER_CACHE.get(jarName);
if (urlClassLoader == null) {
return null;
}
return urlClassLoader.loadClass(name);
}
public void unloadJarFile(String path) throws MalformedURLException {
String jarName = path.substring(path.indexOf("_") + 1);
if (StringUtils.isNotEmpty(jarName) && jarName.endsWith(".jar")) {
jarName = jarName.substring(0, jarName.length() - 4);
}
MsURLClassLoader urlClassLoader = LOADER_CACHE.get(jarName);
if (urlClassLoader == null) {
return;
}
urlClassLoader.unloadJarFile(path);
urlClassLoader = null;
LOADER_CACHE.remove(jarName);
}
}

View File

@ -1,68 +0,0 @@
package io.metersphere.service.utils;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
public class MsURLClassLoader extends URLClassLoader {
private JarURLConnection cachedJarFile = null;
public MsURLClassLoader() {
super(new URL[]{}, findParentClassLoader());
}
/**
* 将指定的文件url添加到类加载器的classpath中去并缓存jar connection方便以后卸载jar
* 一个可想类加载器的classpath中添加的文件url
*
* @param
*/
public void addURLFile(URL file) {
try {
// 打开并缓存文件url连接
URLConnection uc = file.openConnection();
if (uc instanceof JarURLConnection) {
uc.setUseCaches(true);
((JarURLConnection) uc).getManifest();
cachedJarFile = (JarURLConnection) uc;
}
} catch (Exception e) {
System.err.println("Failed to cache plugin JAR file: " + file.toExternalForm());
}
addURL(file);
}
public void unloadJarFile(String url) {
JarURLConnection jarURLConnection = cachedJarFile;
if (jarURLConnection == null) {
return;
}
try {
System.err.println("Unloading plugin JAR file " + jarURLConnection.getJarFile().getName());
jarURLConnection.getJarFile().close();
jarURLConnection = null;
} catch (Exception e) {
System.err.println("Failed to unload JAR file\n" + e);
}
}
/**
* 定位基于当前上下文的父类加载器
*
* @return 返回可用的父类加载器.
*/
private static ClassLoader findParentClassLoader() {
ClassLoader parent = ClassLoader.getSystemClassLoader();
if (parent == null) {
parent = MsURLClassLoader.class.getClassLoader();
}
if (parent == null) {
parent = ClassLoader.getSystemClassLoader();
}
return parent;
}
}

View File

@ -233,7 +233,7 @@
</el-row>
<el-row>
<el-col>
<el-form-item label="Scope" :rules="requiredRules" prop="configuration.scope">
<el-form-item label="Scope" prop="configuration.scope">
<el-input v-model="form.configuration.scope"/>
</el-form-item>
</el-col>

View File

@ -79,11 +79,11 @@ public interface ExtTestCaseMapper {
List<TrackCountResult> countStatus(@Param("projectId") String projectId);
List<TrackCountResult> countRelevance(@Param("projectId") String projectId);
List<TrackCountResult> countRelevance(@Param("projectId") String projectId, @Param("queryUI") boolean queryUI);
long countRelevanceCreatedThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
int countCoverage(@Param("projectId") String projectId);
int countCoverage(@Param("projectId") String projectId, @Param("queryUi") boolean queryUi);
List<TrackCountResult> countFuncMaintainer(@Param("projectId") String projectId);

View File

@ -836,7 +836,14 @@
FROM test_case_test
WHERE test_type = 'automation'
and test_id IN (select id FROM api_scenario WHERE `STATUS` != 'Trash')
)
<if test="queryUI">
UNION
SELECT test_case_id, test_id, test_type
FROM test_case_test
WHERE test_type = 'uiAutomation'
and test_id IN (select id FROM ui_scenario WHERE `STATUS` != 'Trash')
</if>
)
test_case_test
ON test_case.id = test_case_test.test_case_id
WHERE
@ -869,6 +876,11 @@
UNION
SELECT test_case_id FROM test_case_test WHERE test_type = 'automation' and test_id IN (select id FROM
api_scenario WHERE `STATUS` != 'Trash')
<if test="queryUi">
UNION
SELECT test_case_id FROM test_case_test WHERE test_type = 'uiAutomation' and test_id IN (select id FROM
ui_scenario WHERE `STATUS` != 'Trash')
</if>
)
</select>
<select id="countFuncMaintainer" resultType="io.metersphere.dto.TrackCountResult">

View File

@ -1,5 +1,5 @@
package io.metersphere.constants;
public enum TestCaseTestType {
testcase, automation, performance
testcase, automation, performance, uiAutomation
}

View File

@ -2,6 +2,7 @@ package io.metersphere.controller;
import io.metersphere.base.domain.TestCase;
import io.metersphere.commons.constants.MicroServiceName;
import io.metersphere.dto.BugStatistics;
import io.metersphere.dto.TrackCountResult;
import io.metersphere.dto.TrackStatisticsDTO;
@ -9,6 +10,7 @@ import io.metersphere.i18n.Translator;
import io.metersphere.plan.dto.ChartsData;
import io.metersphere.service.TestCaseService;
import io.metersphere.service.TrackService;
import io.metersphere.utils.DiscoveryUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@ -21,7 +23,6 @@ import java.util.List;
@RestController
@RequestMapping("/track")
public class TrackController {
@Resource
private TrackService trackService;
@Resource
@ -61,7 +62,8 @@ public class TrackController {
public TrackStatisticsDTO getRelevanceCount(@PathVariable String projectId) {
TrackStatisticsDTO statistics = new TrackStatisticsDTO();
List<TrackCountResult> relevanceResults = trackService.countRelevance(projectId);
boolean queryUi = DiscoveryUtil.hasService(MicroServiceName.UI_TEST);
List<TrackCountResult> relevanceResults = trackService.countRelevance(projectId, queryUi);
statistics.countRelevance(relevanceResults);
long size = trackService.countRelevanceCreatedThisWeek(projectId);
@ -69,7 +71,7 @@ public class TrackController {
List<TestCase> list = testCaseService.getTestCaseByProjectId(projectId);
long total = list.size();
int coverage = trackService.countCoverage(projectId);
int coverage = trackService.countCoverage(projectId, queryUi);
statistics.setCoverageCount(coverage);
statistics.setUncoverageCount(total - coverage);
@ -82,6 +84,9 @@ public class TrackController {
statistics.setApiCaseCountStr(Translator.get("api_case") + "<br/><br/>" + statistics.getApiCaseCount());
statistics.setPerformanceCaseCountStr(Translator.get("performance_case") + "<br/><br/>" + statistics.getPerformanceCaseCount());
statistics.setScenarioCaseStr(Translator.get("scenario_case") + "<br/><br/>" + statistics.getScenarioCaseCount());
if (queryUi) {
statistics.setUiScenarioCaseStr(Translator.get("ui_scenario_case") + "<br/><br/>" + statistics.getUiScenarioCaseCount());
}
return statistics;
}

View File

@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletRequest;
"/test/plan/uiScenario/case",
"/ui/scenario/module",
"/share/test/plan/uiScenario/case",
"/ui/automation"
"/ui/automation",
"/test/case/relevance/uiScenario"
})
public class TrackUiTestController {
@Resource

View File

@ -1,5 +1,5 @@
package io.metersphere.dto;
public enum TestCaseTestStatus {
performance, api, testcase, automation
performance, api, testcase, automation, uiAutomation
}

View File

@ -69,10 +69,16 @@ public class TrackStatisticsDTO {
*/
private long performanceCaseCount = 0;
/**
* UI场景用例数量统计
*/
private long uiScenarioCaseCount = 0;
private String apiCaseCountStr = StringUtils.EMPTY;
private String scenarioCaseStr = StringUtils.EMPTY;
private String performanceCaseCountStr = StringUtils.EMPTY;
private String uiScenarioCaseStr = StringUtils.EMPTY;
/**
* 本周新增数量
@ -187,6 +193,15 @@ public class TrackStatisticsDTO {
chartData.put(Translator.get("scenario_case"), count);
}
}
if (StringUtils.equalsIgnoreCase(TrackCount.UI_AUTOMATION, countResult.getGroupField())) {
Integer count = chartData.get(Translator.get("ui_scenario_case"));
if (count == null) {
chartData.put(Translator.get("ui_scenario_case"), (int) countResult.getCountNumber());
} else {
count += (int) countResult.getCountNumber();
chartData.put(Translator.get("ui_scenario_case"), count);
}
}
this.allRelevanceCaseCount += countResult.getCountNumber();
}

View File

@ -28,5 +28,8 @@ public class PlanCaseRelevanceRequest {
*/
private List<String> testCaseIds = new ArrayList<>();
/**
* 是否同步关联功能用例下关联的接口场景性能ui用例
*/
private Boolean checked;
}

View File

@ -628,22 +628,30 @@ public class TestPlanService {
List<String> apiCaseIds = new ArrayList<>();
List<String> scenarioIds = new ArrayList<>();
List<String> performanceIds = new ArrayList<>();
buildCaseIdList(list, apiCaseIds, scenarioIds, performanceIds);
startRelevance(request, apiCaseIds, scenarioIds, performanceIds);
List<String> uiScenarioIds = new ArrayList<>();
buildCaseIdList(list, apiCaseIds, scenarioIds, performanceIds, uiScenarioIds);
startRelevance(request, apiCaseIds, scenarioIds, performanceIds, uiScenarioIds);
}
private void startRelevance(PlanCaseRelevanceRequest request, List<String> apiCaseIds, List<String> scenarioIds, List<String> performanceIds) {
private void startRelevance(PlanCaseRelevanceRequest request, List<String> apiCaseIds, List<String> scenarioIds, List<String> performanceIds, List<String> uiScenarioIds) {
Set<String> serviceIdSet = DiscoveryUtil.getServiceIdSet();
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
planTestPlanApiCaseService.relevanceByTestIds(apiCaseIds, request.getPlanId());
planTestPlanScenarioCaseService.relevanceByTestIds(scenarioIds, request.getPlanId());
if (CollectionUtils.isNotEmpty(apiCaseIds)) {
planTestPlanApiCaseService.relevanceByTestIds(apiCaseIds, request.getPlanId());
}
if (CollectionUtils.isNotEmpty(scenarioIds)) {
planTestPlanScenarioCaseService.relevanceByTestIds(scenarioIds, request.getPlanId());
}
}
if (serviceIdSet.contains(MicroServiceName.PERFORMANCE_TEST)) {
if (serviceIdSet.contains(MicroServiceName.PERFORMANCE_TEST) && CollectionUtils.isNotEmpty(performanceIds)) {
planTestPlanLoadCaseService.relevanceByTestIds(performanceIds, request.getPlanId());
}
if (serviceIdSet.contains(MicroServiceName.UI_TEST) && CollectionUtils.isNotEmpty(uiScenarioIds)) {
planTestPlanUiScenarioCaseService.relevanceByTestIds(uiScenarioIds, request.getPlanId());
}
}
private static void buildCaseIdList(List<TestCaseTest> list, List<String> apiCaseIds, List<String> scenarioIds, List<String> performanceIds) {
private static void buildCaseIdList(List<TestCaseTest> list, List<String> apiCaseIds, List<String> scenarioIds, List<String> performanceIds, List<String> uiScenarioIds) {
for (TestCaseTest l : list) {
if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.performance.name())) {
performanceIds.add(l.getTestId());
@ -654,6 +662,9 @@ public class TestPlanService {
if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.automation.name())) {
scenarioIds.add(l.getTestId());
}
if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.uiAutomation.name())) {
uiScenarioIds.add(l.getTestId());
}
}
}

View File

@ -29,7 +29,7 @@ import java.util.*;
@Service
public class PlanTestPlanUiScenarioCaseService extends UiTestService {
private static final String BASE_UEL = "/test/plan/uiScenario/case";
private static final String BASE_URL = "/test/plan/uiScenario/case";
@Resource
private PlanTestPlanScenarioCaseService planTestPlanScenarioCaseService;
@ -41,11 +41,11 @@ public class PlanTestPlanUiScenarioCaseService extends UiTestService {
TestPlanService testPlanService;
public List<String> getExecResultByPlanId(String planId) {
return (List<String>) microService.getForData(serviceName, BASE_UEL + "/plan/exec/result/" + planId);
return (List<String>) microService.getForData(serviceName, BASE_URL + "/plan/exec/result/" + planId);
}
public UiPlanReportDTO getUiReport(ApiPlanReportRequest request) {
return microService.postForData(serviceName, BASE_UEL + "/plan/report", request, UiPlanReportDTO.class);
return microService.postForData(serviceName, BASE_URL + "/plan/report", request, UiPlanReportDTO.class);
}
public void calculatePlanReport(List<String> reportIds, TestPlanSimpleReportDTO report) {
@ -93,20 +93,20 @@ public class PlanTestPlanUiScenarioCaseService extends UiTestService {
private TestPlanScenarioStepCountSimpleDTO getStepCount(List<PlanReportCaseDTO> planReportCaseDTOS) {
return microService.postForData(serviceName, BASE_UEL + "/step/count", planReportCaseDTOS, TestPlanScenarioStepCountSimpleDTO.class);
return microService.postForData(serviceName, BASE_URL + "/step/count", planReportCaseDTOS, TestPlanScenarioStepCountSimpleDTO.class);
}
public List<PlanReportCaseDTO> selectStatusForPlanReport(String planId) {
return microService.getForDataArray(serviceName, BASE_UEL + "/get/report/status/" + planId, PlanReportCaseDTO.class);
return microService.getForDataArray(serviceName, BASE_URL + "/get/report/status/" + planId, PlanReportCaseDTO.class);
}
public void copyPlan(String sourcePlanId, String targetPlanId) {
microService.getForData(serviceName, BASE_UEL + "/plan/copy/" + sourcePlanId + "/" + targetPlanId);
microService.getForData(serviceName, BASE_URL + "/plan/copy/" + sourcePlanId + "/" + targetPlanId);
}
public boolean haveUiCase(String planId) {
try {
return (boolean) microService.getForData(serviceName, BASE_UEL + "/have/ui/case/" + planId);
return (boolean) microService.getForData(serviceName, BASE_URL + "/have/ui/case/" + planId);
} catch (MSException e) {
LogUtil.error(e);
return false;
@ -114,34 +114,38 @@ public class PlanTestPlanUiScenarioCaseService extends UiTestService {
}
public List<TestPlanUiScenario> list(String planId) {
return microService.getForDataArray(serviceName, BASE_UEL + "/list/" + planId, TestPlanUiScenario.class);
return microService.getForDataArray(serviceName, BASE_URL + "/list/" + planId, TestPlanUiScenario.class);
}
public Boolean isCaseExecuting(String planId) {
return (Boolean) microService.getForData(serviceName, BASE_UEL + "/is/executing/" + planId);
return (Boolean) microService.getForData(serviceName, BASE_URL + "/is/executing/" + planId);
}
public List<TestPlanUiScenarioDTO> getFailureListByIds(Set<String> ids) {
return microService.postForDataArray(serviceName, BASE_UEL + "/failure/list", ids, TestPlanUiScenarioDTO.class);
return microService.postForDataArray(serviceName, BASE_URL + "/failure/list", ids, TestPlanUiScenarioDTO.class);
}
public List<ModuleNodeDTO> getNodeByPlanId(List<String> projectIds, String planId) {
return microService.postForDataArray(serviceName, BASE_UEL + "/list/module/" + planId, projectIds, ModuleNodeDTO.class);
return microService.postForDataArray(serviceName, BASE_URL + "/list/module/" + planId, projectIds, ModuleNodeDTO.class);
}
public List<TestPlanUiScenarioDTO> buildResponse(List<TestPlanUiScenarioDTO> uiCases) {
if (CollectionUtils.isEmpty(uiCases)) {
return null;
}
return microService.postForDataArray(serviceName, BASE_UEL + "/build/response", uiCases, TestPlanUiScenarioDTO.class);
return microService.postForDataArray(serviceName, BASE_URL + "/build/response", uiCases, TestPlanUiScenarioDTO.class);
}
public Object relevanceList(ApiScenarioRequest request, int pageNum, int pageSize) {
request.setAllowedRepeatCase(testPlanService.isAllowedRepeatCase(request.getPlanId()));
return microService.postForData(serviceName, BASE_UEL + String.format("/relevance/list/%s/%s", pageNum, pageSize), request);
return microService.postForData(serviceName, BASE_URL + String.format("/relevance/list/%s/%s", pageNum, pageSize), request);
}
public void orderCase(ResetOrderRequest request) {
microService.postForData(serviceName, BASE_UEL + "/edit/order", request);
microService.postForData(serviceName, BASE_URL + "/edit/order", request);
}
public void relevanceByTestIds(List<String> uiScenarioIds, String planId) {
microService.postForData(serviceName, BASE_URL + "/relevance/" + planId, uiScenarioIds);
}
}

View File

@ -9,4 +9,5 @@ public class TrackCount {
public static final String TESTCASE = "testcase";
public static final String PERFORMANCE = "performance";
public static final String AUTOMATION = "automation";
public static final String UI_AUTOMATION = "uiAutomation";
}

View File

@ -35,6 +35,7 @@ import io.metersphere.plan.service.TestPlanTestCaseService;
import io.metersphere.request.OrderRequest;
import io.metersphere.request.ProjectVersionRequest;
import io.metersphere.request.ResetOrderRequest;
import io.metersphere.service.remote.ui.RelevanceUiCaseService;
import io.metersphere.xpack.track.dto.AttachmentRequest;
import io.metersphere.request.member.QueryMemberRequest;
import io.metersphere.request.testcase.*;
@ -145,6 +146,8 @@ public class TestCaseService {
private RelevanceApiCaseService relevanceApiCaseService;
@Resource
private RelevanceLoadCaseService relevanceLoadCaseService;
@Resource
private RelevanceUiCaseService relevanceUiCaseService;
// @Resource
// @Lazy
// private ApiTestCaseService apiTestCaseService;
@ -2593,6 +2596,7 @@ public class TestCaseService {
List<ApiTestCase> apiCases = new ArrayList<>();
List<ApiScenario> apiScenarios = new ArrayList<>();
List<LoadTest> apiLoadTests = new ArrayList<>();
List<UiScenario> uiScenarios = new ArrayList<>();
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
apiCases = relevanceApiCaseService.getApiCaseByIds(
@ -2612,6 +2616,13 @@ public class TestCaseService {
versionIds.addAll(apiLoadTests.stream().map(l -> l.getVersionId()).collect(Collectors.toList()));
}
if (serviceIdSet.contains(MicroServiceName.UI_TEST)) {
uiScenarios = relevanceUiCaseService.getUiCaseByIds(
getTestIds(testCaseTests, TestCaseTestType.uiAutomation.name()));
projectIds.addAll(uiScenarios.stream().map(s -> s.getProjectId()).collect(Collectors.toList()));
versionIds.addAll(uiScenarios.stream().map(l -> l.getVersionId()).collect(Collectors.toList()));
}
projectIds = projectIds.stream().distinct().collect(Collectors.toList());
versionIds = versionIds.stream().distinct().collect(Collectors.toList());
@ -2647,6 +2658,10 @@ public class TestCaseService {
getTestCaseTestDaoList(TestCaseTestType.performance.name(), item.getNum(), item.getName(), item.getId(), projectNameMap.get(item.getProjectId()), versionNameMap.get(item.getVersionId()),
testCaseTestList, testCaseTestsMap);
});
uiScenarios.forEach(item -> {
getTestCaseTestDaoList(TestCaseTestType.uiAutomation.name(), item.getNum(), item.getName(), item.getId(), projectNameMap.get(item.getProjectId()), versionNameMap.get(item.getVersionId()),
testCaseTestList, testCaseTestsMap);
});
return testCaseTestList;
}

View File

@ -94,8 +94,8 @@ public class TrackService {
return extTestCaseMapper.countStatus(projectId);
}
public List<TrackCountResult> countRelevance(String projectId) {
return extTestCaseMapper.countRelevance(projectId);
public List<TrackCountResult> countRelevance(String projectId, boolean queryUI) {
return extTestCaseMapper.countRelevance(projectId, queryUI);
}
public long countRelevanceCreatedThisWeek(String projectId) {
@ -111,8 +111,8 @@ public class TrackService {
}
}
public int countCoverage(String projectId) {
return extTestCaseMapper.countCoverage(projectId);
public int countCoverage(String projectId, boolean queryUi) {
return extTestCaseMapper.countCoverage(projectId, queryUi);
}
public List<ChartsData> getCaseMaintenanceBar(String projectId) {

View File

@ -0,0 +1,17 @@
package io.metersphere.service.remote.ui;
import io.metersphere.base.domain.UiScenario;
import io.metersphere.service.remote.project.TrackUiTestService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class RelevanceUiCaseService extends TrackUiTestService {
private static final String BASE_URL = "/test/case";
public List<UiScenario> getUiCaseByIds(List<String> ids) {
return microService.postForDataArray(serviceName, BASE_URL + "/getUiCaseByIds", ids, UiScenario.class);
}
}

View File

@ -188,6 +188,7 @@ message_task_already_exists=Task recipient already exists
api_case=Api
performance_case=Performance
scenario_case=Scenario
ui_scenario_case=UI Scenario
create_user=Create user
test_case_status=Case status
id_not_rightful=ID is not rightful

View File

@ -161,6 +161,7 @@ message_task_already_exists=任务接收人已经存在
api_case=接口用例
performance_case=性能用例
scenario_case=场景用例
ui_scenario_case=UI用例
test_case_status_error=失败
test_case_status_success=成功
test_case_status_trash=废弃

View File

@ -161,6 +161,7 @@ message_task_already_exists=任務接收人已經存在
api_case=接口用例
performance_case=性能用例
scenario_case=場景用例
ui_scenario_case=UI用例
test_case_status_error=失敗
test_case_status_success=成功
test_case_status_trash=廢棄

View File

@ -233,6 +233,10 @@ export function getTestCaseRelevanceScenarioList(pageNum, pageSize, param) {
return post(BASE_URL + "relevance/scenario/list/" + pageNum + "/" + pageSize, param);
}
export function getTestCaseRelevanceUiScenarioList(pageNum, pageSize, param) {
return post(BASE_URL + "relevance/uiScenario/list/" + pageNum + "/" + pageSize, param);
}
export function getTestCaseRelevanceLoadList(pageNum, pageSize, param) {
return post(BASE_URL + "relevance/load/list/" + pageNum + "/" + pageSize, param);
}
@ -245,6 +249,10 @@ export function saveCaseRelevanceScenario(caseId, param) {
return post(BASE_URL + "relate/test/automation/" + caseId, param);
}
export function saveUiCaseRelevanceScenario(caseId, param) {
return post(BASE_URL + "relate/test/uiAutomation/" + caseId, param);
}
export function saveCaseRelevanceLoad(caseId, param) {
return post(BASE_URL + "relate/test/performance/" + caseId, param);
}

View File

@ -0,0 +1,215 @@
<template>
<div>
<div class="right-search">
<version-select v-xpack :project-id="projectId" @changeVersion="changeVersion" margin-right="20"/>
<el-input :placeholder="$t('commons.search_by_name_or_id')" @blur="initTable"
@keyup.enter.native="initTable" class="search-input" size="small" v-model="condition.name"/>
<ms-table-adv-search-bar :condition.sync="condition" class="adv-search-bar"
v-if="condition.components !== undefined && condition.components.length > 0"
@search="initTable"/>
</div>
<ms-table v-loading="result.loading" :data="tableData" :select-node-ids="selectNodeIds" :condition="condition"
:page-size="pageSize"
:total="total"
:showSelectAll="false"
:screenHeight="screenHeight"
@selectCountChange="selectCountChange"
@refresh="initTable"
ref="table">
<ms-table-column
prop="num"
label="ID"
width="100px"
sortable=true>
</ms-table-column>
<ms-table-column
prop="name"
:label="$t('api_test.automation.scenario_name')"/>
<ms-table-column
prop="level"
sortable
min-width="130px"
:label="$t('api_test.automation.case_level')">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.level"/>
</template>
</ms-table-column>
<ms-table-column prop="status"
:label="$t('test_track.plan.plan_status')"
sortable
min-width="120px">
<template v-slot:default="scope">
<plan-status-table-item :value="scope.row.status"/>
</template>
</ms-table-column>
<ms-table-column prop="tags" width="120px" :label="$t('commons.tag')">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" style="margin-left: 0px; margin-right: 2px"/>
<span></span>
</template>
</ms-table-column>
</ms-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</div>
</template>
<script>
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import PriorityTableItem from "@/business/common/tableItems/planview/PriorityTableItem";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import PlanStatusTableItem from "@/business/common/tableItems/plan/PlanStatusTableItem";
import MsTableAdvSearchBar from "metersphere-frontend/src/components/search/MsTableAdvSearchBar";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {TEST_CASE_RELEVANCE_API_CASE_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import {getVersionFilters} from "@/business/utils/sdk-utils";
import MxVersionSelect from "metersphere-frontend/src/components/version/MxVersionSelect";
import {getTestCaseRelevanceScenarioList, getTestCaseRelevanceUiScenarioList} from "@/api/testCase";
export default {
name: "TestCaseRelateUiScenarioList",
components: {
PlanStatusTableItem,
MsTablePagination,
PriorityTableItem,
MsTable,
MsTableColumn,
MsTableAdvSearchBar,
MsTag,
'VersionSelect': MxVersionSelect,
},
data() {
return {
condition: {
components: TEST_CASE_RELEVANCE_API_CASE_CONFIGS
},
result: {},
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
screenHeight: 'calc(100vh - 400px)',//
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
versionFilters: []
}
},
props: {
selectNodeIds: Array,
projectId: String,
versionEnable: Boolean,
notInIds: {
type: Array,
default: null
},
testCaseId: String
},
created: function () {
this.getVersionOptions();
},
watch: {
selectNodeIds() {
this.initTable();
},
projectId() {
this.condition.versionId = null;
this.initTable();
}
},
computed: {
selectRows() {
if (this.$refs.table) {
return this.$refs.table.getSelectRows();
} else {
return new Set();
}
}
},
methods: {
selectCountChange(data) {
this.$emit("selectCountChange", data);
},
initTable(projectId) {
this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds;
if (projectId != null && typeof projectId === 'string') {
this.condition.projectId = projectId;
} else if (this.projectId != null) {
this.condition.projectId = this.projectId;
}
this.condition.notInIds = this.notInIds;
this.condition.testCaseId = this.testCaseId;
getTestCaseRelevanceUiScenarioList(this.currentPage, this.pageSize, this.condition)
.then(response => {
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
this.tableData.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
});
},
clear() {
if (this.$refs.table) {
this.$refs.table.clear();
}
},
buildPagePath(path) {
return path + this.currentPage + "/" + this.pageSize;
},
getSelectIds() {
return this.$refs.table.selectIds;
},
clearSelection() {
if (this.$refs.table) {
this.$refs.table.clearSelectRows();
}
},
getVersionOptions() {
getVersionFilters(this.projectId)
.then(r => this.versionFilters = r.data);
},
changeVersion(currentVersion) {
this.condition.versionId = currentVersion || null;
this.initTable();
}
},
}
</script>
<style scoped>
.right-search {
float: right;
display: inline-block;
}
.search-input {
width: 300px;
margin-right: 20px;
}
.adv-search-bar {
float: right;
margin-top: 5px;
margin-right: 10px;
}
</style>

View File

@ -8,6 +8,7 @@
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="api">{{ $t('api_test.home_page.failed_case_list.table_value.case_type.api') }}</el-dropdown-item>
<el-dropdown-item command="scenario">{{ $t('api_test.home_page.failed_case_list.table_value.case_type.scene') }}</el-dropdown-item>
<el-dropdown-item command="ui" v-xpack>{{ $t('api_test.home_page.failed_case_list.table_value.case_type.ui') }}</el-dropdown-item>
<el-dropdown-item command="performance">{{$t('api_test.home_page.failed_case_list.table_value.case_type.load')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -61,6 +62,13 @@
@refresh="initTable"
ref="apiScenarioRelevance"/>
<test-case-ui-scenario-relate
:case-id="caseId"
:versionEnable="versionEnable"
:not-in-ids="notInIds"
@refresh="initTable"
ref="uiScenarioRelevance"/>
<test-case-load-relate
:case-id="caseId"
:not-in-ids="notInIds"
@ -77,11 +85,15 @@ import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColu
import TestCaseApiRelate from "@/business/case/components/TestCaseApiRelate";
import {deleteRelateTest, getRelateTest} from "@/api/testCase";
import TestCaseScenarioRelate from "@/business/case/components/TestCaseScenarioRelate";
import TestCaseUiScenarioRelate from "@/business/case/components/TestCaseUiScenarioRelate";
import TestCaseLoadRelate from "@/business/case/components/TestCaseLoadRelate";
import i18n from "@/i18n";
import TestCaseUiScenarioRelevance from "@/business/plan/view/comonents/ui/TestCaseUiScenarioRelevance";
export default {
name: "TestCaseTestRelate",
components: {TestCaseLoadRelate, TestCaseScenarioRelate, TestCaseApiRelate, MsTableColumn, MsTable},
components: {
TestCaseUiScenarioRelevance,
TestCaseLoadRelate, TestCaseScenarioRelate, TestCaseApiRelate, MsTableColumn, MsTable, TestCaseUiScenarioRelate},
data() {
return {
data: [],
@ -89,7 +101,8 @@ export default {
typeMap: {
testcase: this.$t('api_test.home_page.failed_case_list.table_value.case_type.api'),
automation: this.$t('api_test.home_page.failed_case_list.table_value.case_type.scene'),
performance: this.$t('api_test.home_page.failed_case_list.table_value.case_type.load')
performance: this.$t('api_test.home_page.failed_case_list.table_value.case_type.load'),
uiAutomation: this.$t('api_test.home_page.failed_case_list.table_value.case_type.ui')
},
operators: [
{
@ -122,6 +135,8 @@ export default {
this.$refs.apiScenarioRelevance.open();
} else if (key === 'performance') {
this.$refs.loadRelevance.open();
} else if (key === 'ui') {
this.$refs.uiScenarioRelevance.open();
}
},
remove(row) {

View File

@ -0,0 +1,117 @@
<template>
<test-case-relevance-base
@setProject="setProject"
@save="saveCaseRelevance"
ref="baseRelevance">
<template v-slot:aside>
<ui-scenario-module
:show-case-num="false"
:relevance-project-id="projectId"
@nodeSelectEvent="nodeChange"
@refreshTable="refresh"
@setModuleOptions="setModuleOptions"
@enableTrash="false"
:is-read-only="true"
ref="nodeTree"/>
</template>
<test-case-relate-ui-scenario-list
:select-node-ids="selectNodeIds"
:project-id="projectId"
:not-in-ids="notInIds"
:versionEnable="versionEnable"
:test-case-id="caseId"
@selectCountChange="setSelectCounts"
ref="apiCaseList"/>
</test-case-relevance-base>
</template>
<script>
import TestCaseRelateApiList from "@/business/case/components/TestCaseRelateApiList";
import TestCaseRelevanceBase from "@/business/plan/view/comonents/base/TestCaseRelevanceBase";
import UiScenarioModule from "@/business/plan/view/comonents/ui/UiScenarioModule";
import TestCaseRelateUiScenarioList from "@/business/case/components/TestCaseRelateUiScenarioList";
import {saveUiCaseRelevanceScenario} from "@/api/testCase";
export default {
name: "TestCaseUiScenarioRelate",
components: {
TestCaseRelateUiScenarioList,
UiScenarioModule,
TestCaseRelevanceBase,
TestCaseRelateApiList,
},
data() {
return {
selectNodeIds: [],
moduleOptions: {},
condition: {},
projectId: ""
};
},
props: {
caseId: {
type: String
},
versionEnable: {
type: Boolean,
default: false
},
notInIds: {
type: Array,
default: null
}
},
methods: {
open() {
this.init();
this.$refs.baseRelevance.open();
if (this.$refs.apiCaseList) {
this.$refs.apiCaseList.clear();
}
},
init() {
if (this.$refs.apiCaseList) {
this.$refs.apiCaseList.initTable();
}
if (this.$refs.nodeTree) {
this.$refs.nodeTree.list();
}
},
setProject(projectId) {
this.projectId = projectId;
},
refresh(data) {
this.$refs.apiCaseList.initTable(data);
},
nodeChange(node, nodeIds, pNodes) {
this.selectNodeIds = nodeIds;
},
setModuleOptions(data) {
this.moduleOptions = data;
},
saveCaseRelevance() {
let ids = this.$refs.apiCaseList.getSelectIds();
saveUiCaseRelevanceScenario(this.caseId, ids)
.then(() => {
this.$success(this.$t('commons.save_success'));
this.$emit('refresh');
this.$refs.baseRelevance.close();
});
},
setSelectCounts(data) {
this.$refs.baseRelevance.selectCounts = data;
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,4 +1,4 @@
<template>
<template>
<div class="dashboard-card">
<el-card shadow="never" class="box-card" style="height: 100%">
<div slot="header" class="clearfix">

View File

@ -59,7 +59,9 @@
shareId,
isPlanReport: true,
isTemplate,
response
response,
showCancelButton: false,
showReportNameButton: false
}"/>
<UiShareReportDetail
v-else

View File

@ -165,7 +165,8 @@
<!-- 执行结果 -->
<el-drawer :visible.sync="runVisible" :destroy-on-close="true" direction="ltr" :withHeader="true" :modal="false"
size="90%">
<micro-app :to="`/ui/report/view/${reportId}?showCancelButton=false`" service="ui"/>
<micro-app :to="`/ui/report/view/${reportId}`" route-name="ApiReportView"
:route-params="{showCancelButton: false, reportId}" service="ui"/>
</el-drawer>
</div>
</el-card>

View File

@ -35,6 +35,7 @@ const TRACK_HEADER = {
{id: 'testPlanTestCaseCount', key: 'g', label: 'test_track.plan.test_plan_test_case_count'},
{id: 'testPlanApiCaseCount', key: 'h', label: 'test_track.plan.test_plan_api_case_count'},
{id: 'testPlanApiScenarioCount', key: 'i', label: 'test_track.plan.test_plan_api_scenario_count'},
{id: 'testPlanUiScenarioCount', key: 'l', label: 'test_track.plan.test_plan_ui_scenario_count', xpack: true},
{id: 'testPlanLoadCaseCount', key: 'j', label: 'test_track.plan.test_plan_load_case_count'},
{id: 'principalName', key: 'k', label: 'test_track.plan.plan_principal'},
],