fix(测试跟踪): UI测试用例列表批量执行后执行结果未更新
--bug=1018918 --user=李玉号 【UI测试】测试跟踪-测试计划-UI测试用例列表-操作-执行/批量执行后执行结果未更新 https://www.tapd.cn/55049933/s/1347660
This commit is contained in:
parent
3f00e66597
commit
0ae8294305
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class TestPlanCaseStatusDTO {
|
||||
private String planCaseId;
|
||||
private String planCaseStatus;
|
||||
}
|
|
@ -7,7 +7,7 @@ import io.metersphere.base.mapper.ApiExecutionQueueDetailMapper;
|
|||
import io.metersphere.base.mapper.ApiExecutionQueueMapper;
|
||||
import io.metersphere.commons.constants.KafkaTopicConstants;
|
||||
import io.metersphere.commons.constants.TestPlanReportStatus;
|
||||
import io.metersphere.plan.service.TestCaseSyncStatusService;
|
||||
import io.metersphere.plan.service.AutomationCaseExecOverService;
|
||||
import io.metersphere.plan.service.TestPlanReportService;
|
||||
import io.metersphere.utils.LoggerUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
|
@ -30,7 +30,7 @@ public class ExecReportListener {
|
|||
@Resource
|
||||
private TestPlanReportService testPlanReportService;
|
||||
@Resource
|
||||
private TestCaseSyncStatusService testCaseSyncStatusService;
|
||||
private AutomationCaseExecOverService automationCaseExecOverService;
|
||||
|
||||
@KafkaListener(id = CONSUME_ID, topics = KafkaTopicConstants.TEST_PLAN_REPORT_TOPIC, groupId = "${spring.application.name}")
|
||||
public void consume(ConsumerRecord<?, String> record) {
|
||||
|
@ -45,14 +45,9 @@ public class ExecReportListener {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试计划相关的自动化用例结束后,会根据测试计划的配置判断是否要同步功能用例的状态
|
||||
* 目前暂时只有这一个需求。后续如果有了更多操作,建议将该方法内的逻辑处理放入一个共有方法中。
|
||||
*
|
||||
* @param testId
|
||||
*/
|
||||
public void automationCaseTestEnd(String testId) {
|
||||
testCaseSyncStatusService.checkAndUpdateFunctionCaseStatus(testId);
|
||||
//自动化用例执行完成之后的后续操作
|
||||
automationCaseExecOverService.automationCaseExecOver(testId);
|
||||
}
|
||||
|
||||
public void testPlanReportTestEnded(String testPlanReportId) {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package io.metersphere.plan.service;
|
||||
|
||||
import io.metersphere.base.domain.TestPlanApiCase;
|
||||
import io.metersphere.base.domain.TestPlanApiScenario;
|
||||
import io.metersphere.base.domain.TestPlanLoadCase;
|
||||
import io.metersphere.base.domain.TestPlanUiScenario;
|
||||
import io.metersphere.base.mapper.ext.*;
|
||||
import io.metersphere.dto.TestPlanCaseStatusDTO;
|
||||
import io.metersphere.utils.JsonUtils;
|
||||
import io.metersphere.websocket.UICaseStatusHandleSocket;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class AutomationCaseExecOverService {
|
||||
|
||||
@Resource
|
||||
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
|
||||
@Resource
|
||||
private ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper;
|
||||
@Resource
|
||||
private ExtTestPlanLoadCaseMapper extTestPlanLoadCaseMapper;
|
||||
@Resource
|
||||
private ExtTestPlanUiCaseMapper extTestPlanUiCaseMapper;
|
||||
@Resource
|
||||
private TestCaseSyncStatusService testCaseSyncStatusService;
|
||||
|
||||
public void automationCaseExecOver(String testId) {
|
||||
TestPlanApiCase testPlanApiCase = null;
|
||||
TestPlanApiScenario testPlanApiScenario = null;
|
||||
TestPlanLoadCase testPlanLoadCase = null;
|
||||
TestPlanUiScenario testPlanUiScenario = null;
|
||||
|
||||
testPlanApiCase = extTestPlanApiCaseMapper.selectBaseInfoById(testId);
|
||||
if (testPlanApiCase == null) {
|
||||
testPlanApiScenario = extTestPlanScenarioCaseMapper.selectBaseInfoById(testId);
|
||||
}
|
||||
if (ObjectUtils.allNull(testPlanApiCase, testPlanApiScenario)) {
|
||||
testPlanLoadCase = extTestPlanLoadCaseMapper.selectBaseInfoById(testId);
|
||||
}
|
||||
if (ObjectUtils.allNull(testPlanApiCase, testPlanApiScenario, testPlanLoadCase)) {
|
||||
testPlanUiScenario = extTestPlanUiCaseMapper.selectBaseInfoById(testId);
|
||||
}
|
||||
|
||||
String automationCaseId = null, planId = null;
|
||||
String triggerCaseExecResult = null;
|
||||
if (testPlanApiCase != null) {
|
||||
automationCaseId = testPlanApiCase.getApiCaseId();
|
||||
planId = testPlanApiCase.getTestPlanId();
|
||||
triggerCaseExecResult = testPlanApiCase.getStatus();
|
||||
} else if (testPlanApiScenario != null) {
|
||||
automationCaseId = testPlanApiScenario.getApiScenarioId();
|
||||
planId = testPlanApiScenario.getTestPlanId();
|
||||
triggerCaseExecResult = testPlanApiScenario.getLastResult();
|
||||
} else if (testPlanLoadCase != null) {
|
||||
automationCaseId = testPlanLoadCase.getLoadCaseId();
|
||||
planId = testPlanLoadCase.getTestPlanId();
|
||||
triggerCaseExecResult = testPlanLoadCase.getStatus();
|
||||
} else if (testPlanUiScenario != null) {
|
||||
automationCaseId = testPlanUiScenario.getUiScenarioId();
|
||||
planId = testPlanUiScenario.getTestPlanId();
|
||||
triggerCaseExecResult = testPlanUiScenario.getLastResult();
|
||||
}
|
||||
|
||||
if (StringUtils.isNoneEmpty(automationCaseId, planId, triggerCaseExecResult)) {
|
||||
//检查是否需要自动更新功能用例状态
|
||||
testCaseSyncStatusService.updateFunctionCaseStatusByAutomationCaseId(automationCaseId, planId, triggerCaseExecResult);
|
||||
}
|
||||
if (testPlanUiScenario != null) {
|
||||
//UI执行完成发送Socket
|
||||
UICaseStatusHandleSocket.sendMessageSingle(planId, JsonUtils.toJSONString(TestPlanCaseStatusDTO.builder().planCaseId(testPlanUiScenario.getId()).planCaseStatus(triggerCaseExecResult)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ import io.metersphere.utils.BatchProcessingUtil;
|
|||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -112,49 +111,9 @@ public class TestCaseSyncStatusService {
|
|||
}
|
||||
}
|
||||
|
||||
public void checkAndUpdateFunctionCaseStatus(String testId) {
|
||||
TestPlanApiCase testPlanApiCase = null;
|
||||
TestPlanApiScenario testPlanApiScenario = null;
|
||||
TestPlanLoadCase testPlanLoadCase = null;
|
||||
TestPlanUiScenario testPlanUiScenario = null;
|
||||
|
||||
testPlanApiCase = extTestPlanApiCaseMapper.selectBaseInfoById(testId);
|
||||
if (testPlanApiCase == null) {
|
||||
testPlanApiScenario = extTestPlanScenarioCaseMapper.selectBaseInfoById(testId);
|
||||
}
|
||||
if (ObjectUtils.allNull(testPlanApiCase, testPlanApiScenario)) {
|
||||
testPlanLoadCase = extTestPlanLoadCaseMapper.selectBaseInfoById(testId);
|
||||
}
|
||||
if (ObjectUtils.allNull(testPlanApiCase, testPlanApiScenario, testPlanLoadCase)) {
|
||||
testPlanUiScenario = extTestPlanUiCaseMapper.selectBaseInfoById(testId);
|
||||
}
|
||||
|
||||
String automationCaseId = null, planId = null;
|
||||
String triggerCaseExecResult = null;
|
||||
if (testPlanApiCase != null) {
|
||||
automationCaseId = testPlanApiCase.getApiCaseId();
|
||||
planId = testPlanApiCase.getTestPlanId();
|
||||
triggerCaseExecResult = testPlanApiCase.getStatus();
|
||||
} else if (testPlanApiScenario != null) {
|
||||
automationCaseId = testPlanApiScenario.getApiScenarioId();
|
||||
planId = testPlanApiScenario.getTestPlanId();
|
||||
triggerCaseExecResult = testPlanApiScenario.getLastResult();
|
||||
} else if (testPlanLoadCase != null) {
|
||||
automationCaseId = testPlanLoadCase.getLoadCaseId();
|
||||
planId = testPlanLoadCase.getTestPlanId();
|
||||
triggerCaseExecResult = testPlanLoadCase.getStatus();
|
||||
} else if (testPlanUiScenario != null) {
|
||||
automationCaseId = testPlanUiScenario.getUiScenarioId();
|
||||
planId = testPlanUiScenario.getTestPlanId();
|
||||
triggerCaseExecResult = testPlanUiScenario.getLastResult();
|
||||
}
|
||||
|
||||
if (StringUtils.isNoneEmpty(automationCaseId, planId, triggerCaseExecResult)) {
|
||||
this.updateFunctionCaseStatusByAutomationCaseId(automationCaseId, planId, triggerCaseExecResult);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFunctionCaseStatusByAutomationCaseId(String automationCaseId, String testPlanId, String triggerCaseRunResult) {
|
||||
public void updateFunctionCaseStatusByAutomationCaseId(String automationCaseId, String testPlanId, String triggerCaseRunResult) {
|
||||
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId);
|
||||
if (testPlan != null && testPlan.getAutomaticStatusUpdate()) {
|
||||
HttpHeaderUtils.runAsUser(baseUserService.getUserDTO(testPlan.getCreator()));
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package io.metersphere.websocket;
|
||||
|
||||
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.utils.LoggerUtil;
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 推送消息更新UI列表用例状态
|
||||
*/
|
||||
|
||||
@Component
|
||||
@ServerEndpoint("/websocket/plan/ui/{planId}")
|
||||
public class UICaseStatusHandleSocket {
|
||||
|
||||
public static final Map<String, Set<Session>> ONLINE_PLAN_UI_SESSIONS = new ConcurrentHashMap<>();
|
||||
|
||||
public static void sendMessage(Session session, String message) {
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
// 替换了web容器后 jetty没有设置永久有效的参数,这里暂时设置超时时间为一天
|
||||
session.setMaxIdleTimeout(86400000L);
|
||||
RemoteEndpoint.Async async = session.getAsyncRemote();
|
||||
if (async == null) {
|
||||
return;
|
||||
}
|
||||
async.sendText(message);
|
||||
}
|
||||
|
||||
public static void sendMessageSingle(String planId, String message) {
|
||||
ONLINE_PLAN_UI_SESSIONS.get(planId).forEach(session -> sendMessage(session, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接成功响应
|
||||
*/
|
||||
@OnOpen
|
||||
public void openSession(@PathParam("planId") String planId, Session session) {
|
||||
Set<Session> sessions = ONLINE_PLAN_UI_SESSIONS.getOrDefault(planId, new HashSet<>());
|
||||
sessions.add(session);
|
||||
ONLINE_PLAN_UI_SESSIONS.put(planId, sessions);
|
||||
sendMessage(session, "CONN_SUCCEEDED");
|
||||
LoggerUtil.info("客户端: [" + planId + "] : 连接成功!" + ONLINE_PLAN_UI_SESSIONS.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到消息响应
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage(@PathParam("planId") String planId, String message) {
|
||||
LoggerUtil.info("服务器收到:[" + planId + "] : " + message);
|
||||
sendMessageSingle(planId, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接关闭响应
|
||||
*/
|
||||
@OnClose
|
||||
public void onClose(@PathParam("planId") String planId, Session session) throws IOException {
|
||||
//当前的Session 移除
|
||||
Set<Session> sessions = ONLINE_PLAN_UI_SESSIONS.get(planId);
|
||||
if (CollectionUtils.isNotEmpty(sessions)) {
|
||||
LogUtil.info("剔除一个socket链接: {} - {}", planId, session.getId());
|
||||
sessions.remove(session);
|
||||
LogUtil.info("在线socket链接: {}, size: {}", planId, sessions.size());
|
||||
} else {
|
||||
ONLINE_PLAN_UI_SESSIONS.remove(planId);
|
||||
LoggerUtil.info("[" + planId + "] : 断开连接!" + ONLINE_PLAN_UI_SESSIONS.size());
|
||||
//并且通知其他人当前用户已经断开连接了
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接异常响应
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Session session, Throwable throwable) throws IOException {
|
||||
session.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 每一分钟群发一次心跳检查
|
||||
*/
|
||||
@Scheduled(cron = "0 0/1 * * * ?")
|
||||
public void heartbeatCheck() {
|
||||
for (Set<Session> sessions : ONLINE_PLAN_UI_SESSIONS.values()) {
|
||||
sessions.forEach(session -> sendMessage(session, "heartbeat check"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -221,6 +221,7 @@ import i18n from "@/i18n";
|
|||
import MicroApp from "metersphere-frontend/src/components/MicroApp";
|
||||
import MsTestPlanApiStatus from "@/business/plan/view/comonents/api/TestPlanApiStatus";
|
||||
import UiRunMode from "@/business/plan/view/comonents/ui/UiRunMode";
|
||||
import {baseSocket} from "@/api/base-network";
|
||||
|
||||
export default {
|
||||
name: "MsTestPlanUiScenarioList",
|
||||
|
@ -340,7 +341,13 @@ export default {
|
|||
return editTestPlanUiScenarioCaseOrder;
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.websocket && this.websocket.close instanceof Function) {
|
||||
this.websocket.close();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initWebSocket();
|
||||
this.search();
|
||||
this.getVersionOptions();
|
||||
},
|
||||
|
@ -354,6 +361,34 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
initWebSocket() {
|
||||
this.websocket = baseSocket("/plan/ui/" + this.planId);
|
||||
this.websocket.onmessage = this.onMessage;
|
||||
this.websocket.onopen = this.onOpen;
|
||||
this.websocket.onerror = this.onError;
|
||||
this.websocket.onclose = this.onClose;
|
||||
},
|
||||
onOpen() {
|
||||
},
|
||||
onError() {
|
||||
},
|
||||
onMessage(e) {
|
||||
if (e.data === 'CONN_SUCCEEDED') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let obj = JSON.parse(e.data);
|
||||
let {planCaseId, planCaseStatus} = obj;
|
||||
let data = this.tableData.filter(d => d.id === planCaseId);
|
||||
if (data.length > 0) {
|
||||
data[0]['lastResult'] = planCaseStatus;
|
||||
}
|
||||
} catch (ex) {
|
||||
// nothing
|
||||
}
|
||||
},
|
||||
onClose() {
|
||||
},
|
||||
filterSearch() {
|
||||
// 添加搜索条件时,当前页设置成第一页
|
||||
this.currentPage = 1;
|
||||
|
|
Loading…
Reference in New Issue