feat (接口自动化): 完成用例和场景执行历史部分功能 #1001749

This commit is contained in:
fit2-zhao 2021-07-29 17:30:09 +08:00 committed by fit2-zhao
parent c3da765181
commit c45e532c20
21 changed files with 559 additions and 439 deletions

View File

@ -14,4 +14,5 @@ public class ApiTestCaseDTO extends ApiTestCase {
private String createUser;
private String deleteUser;
private String apiName;
private String passRate;
}

View File

@ -11,6 +11,7 @@ public class ApiTestCaseResult extends ApiTestCaseWithBLOBs {
private String createUser;
private String updateUser;
private String execResult;
private String passRate;
private String apiMethod;
private Long execTime;
private boolean active = false;

View File

@ -70,9 +70,9 @@ public class MsLoopController extends MsTestElement {
groupTree.add(resultAction);
}
// 循环间隔时长设置
ConstantTimer cnstantTimer = getCnstantTimer();
if (cnstantTimer != null) {
groupTree.add(cnstantTimer);
ConstantTimer constantTimer = getConstantTimer();
if (constantTimer != null) {
groupTree.add(constantTimer);
}
if (CollectionUtils.isNotEmpty(hashTree)) {
@ -238,7 +238,7 @@ public class MsLoopController extends MsTestElement {
return null;
}
private ConstantTimer getCnstantTimer() {
private ConstantTimer getConstantTimer() {
ConstantTimer constantTimer = new ConstantTimer();
constantTimer.setEnabled(this.isEnable());
constantTimer.setProperty(TestElement.TEST_CLASS, ConstantTimer.class.getName());

View File

@ -100,7 +100,7 @@ public class ApiDefinitionExecResultService {
saveResult.setStartTime(item.getStartTime());
saveResult.setEndTime(item.getResponseResult().getResponseTime());
// 清空上次执行结果的内容只保留当前最新一条内容
// 清空上次执行结果的内容只保留近五条结果
ApiDefinitionExecResult prevResult = extApiDefinitionExecResultMapper.selectMaxResultByResourceIdAndType(item.getName(), type);
if (prevResult != null) {
prevResult.setContent(null);

View File

@ -12,9 +12,8 @@
<select id="selectMaxResultByResourceIdAndType"
resultType="io.metersphere.base.domain.ApiDefinitionExecResult">
select * from api_definition_exec_result
where resource_id = #{resourceId,jdbcType=VARCHAR}
and `type` = #{type, jdbcType=VARCHAR}
ORDER BY start_time DESC LIMIT 1
where resource_id = #{resourceId,jdbcType=VARCHAR} and `type` = #{type, jdbcType=VARCHAR}
ORDER BY start_time DESC LIMIT 5, 1
</select>
<select id="countByProjectIDAndCreateInThisWeek" resultType="java.lang.Long">

View File

@ -276,7 +276,7 @@
<select id="selectPreviousReportByScenarioId" resultType="io.metersphere.base.domain.ApiScenarioReport">
select * from api_scenario_report
WHERE execute_type in ("Completed","Debug") and scenario_id=#{scenarioId} and id != #{nowId} ORDER BY create_time desc LIMIT 1
WHERE execute_type in ("Completed","Debug") and scenario_id=#{scenarioId} and id != #{nowId} ORDER BY create_time desc LIMIT 5,1
</select>
</mapper>

View File

@ -235,8 +235,8 @@
<select id="listSimple" resultType="io.metersphere.api.dto.definition.ApiTestCaseDTO">
select
t1.id, t1.project_id, t1.name, t1.api_definition_id, t1.priority, t1.description, t1.create_user_id, t1.update_user_id, t1.create_time, t1.update_time, t1.num,
a.module_id, a.path, a.protocol, t1.tags,t1.status,
t1.delete_time, deleteUser.name AS deleteUser
a.module_id, a.path, a.protocol, t1.tags,t1.status,t1.last_result_id as lastResultId,
t1.delete_time, deleteUser.name AS deleteUser,CONCAT(FORMAT(SUM(IF (t2.`status`='success',1,0))/COUNT(t2.id)*100,2),'%') passRate
from
api_test_case t1
LEFT JOIN api_definition_exec_result t2 ON t1.last_result_id = t2.id
@ -248,70 +248,70 @@
<if test="request.apiDefinitionId != null and request.apiDefinitionId!=''">
and t1.api_definition_id = #{request.apiDefinitionId}
</if>
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
GROUP BY t1.id
having 1=1
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.projectId != null and request.projectId!=''">
and t1.project_id = #{request.projectId}
</if>
<if test="request.id != null and request.id!=''">
and t1.id = #{request.id}
</if>
<if test="request.ids != null and request.ids.size() > 0">
<if test="request.projectId != null and request.projectId!=''">
and t1.project_id = #{request.projectId}
and
</if>
<if test="request.id != null and request.id!=''">
and t1.id = #{request.id}
</if>
<if test="request.ids != null and request.ids.size() > 0">
<if test="request.projectId != null and request.projectId!=''">
and
t1.id in
<foreach collection="request.ids" item="caseId" separator="," open="(" close=")">
#{caseId}
</foreach>
</if>
<if test="request.name != null and request.name!=''">
and (t1.name like CONCAT('%', #{request.name},'%')
or t1.tags like CONCAT('%', #{request.name},'%')
or t1.num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.createTime > 0">
and t1.create_time >= #{request.createTime}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and a.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'priority'">
and t1.priority in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='status'">
and t1.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
t1.id in
<foreach collection="request.ids" item="caseId" separator="," open="(" close=")">
#{caseId}
</foreach>
</if>
<if test="request.name != null and request.name!=''">
and (t1.name like CONCAT('%', #{request.name},'%')
or t1.tags like CONCAT('%', #{request.name},'%')
or t1.num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.createTime > 0">
and t1.create_time >= #{request.createTime}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and a.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'priority'">
and t1.priority in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='status'">
and t1.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
<if test="key=='status' and values.size == 0">
and (t1.status is null or t1.status != 'Trash')
</if>
</foreach>
</if>
<if test="request.filters == null || request.filters.size() == 0 ">
and (t1.status is null or t1.status != 'Trash')
</if>
</where>
<if test="key=='status' and values.size == 0">
and (t1.status is null or t1.status != 'Trash')
</if>
</foreach>
</if>
<if test="request.filters == null || request.filters.size() == 0 ">
and (t1.status is null or t1.status != 'Trash')
</if>
<if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">

View File

@ -9,8 +9,12 @@ import java.util.List;
public interface ExtTaskMapper {
void deleteByResourceId(String id);
List<TaskCenterDTO> getTasks (@Param("request") TaskCenterRequest request);
int getRunningTasks (@Param("request") TaskCenterRequest request);
List<TaskCenterDTO> getTasks(@Param("request") TaskCenterRequest request);
int getRunningTasks(@Param("request") TaskCenterRequest request);
List<TaskCenterDTO> getCases(@Param("id") String id);
List<TaskCenterDTO> getScenario(@Param("id") String id);
}

View File

@ -51,6 +51,22 @@
)tt ORDER BY tt.executionTime DESC;
</select>
<select id="getCases" resultType="io.metersphere.task.dto.TaskCenterDTO" parameterType="java.lang.String">
select t.id,t.name,'API' as executionModule, ifnull(t2.name,'LOCAL') as actuator, t1.`name` as executor,t.create_time as executionTime, ifnull(t.trigger_mode,'MANUAL') as triggerMode ,ifnull(t.status,'Saved') as executionStatus
from api_definition_exec_result t
left join `user` t1 ON t.user_id = t1.id
left join test_resource_pool t2 on t.actuator = t2.id
left join api_test_case t4 on t4.id = t.resource_id
where t4.id=#{id} and t.content != "" LIMIT 5;
</select>
<select id="getScenario" resultType="io.metersphere.task.dto.TaskCenterDTO" parameterType="java.lang.String">
select t.id,if(t.scenario_id like "[\"%\"]", t.name,t.scenario_name) as name ,'SCENARIO' as executionModule, ifnull(t2.name,'LOCAL') as actuator, t1.`name` as executor,t.create_time as executionTime, t.trigger_mode as triggerMode ,t.status as executionStatus
from api_scenario_report t
left join `user` t1 ON t.user_id = t1.id
left join test_resource_pool t2 on t.actuator = t2.id
where t.execute_type ='Debug' and t.scenario_id=#{id} ORDER BY t.create_time desc LIMIT 5;
</select>
<select id="getRunningTasks" resultType="java.lang.Integer" parameterType="java.lang.String">
SELECT count(tt.id) FROM (

View File

@ -4,10 +4,7 @@ import io.metersphere.task.dto.TaskCenterDTO;
import io.metersphere.task.dto.TaskCenterRequest;
import io.metersphere.task.service.TaskService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@ -24,6 +21,18 @@ public class TaskController {
return taskService.getTasks(request);
}
@GetMapping("/case/{id}")
@RequiresPermissions("PROJECT_API_SCENARIO:READ")
public List<TaskCenterDTO> getCase(@PathVariable String id) {
return taskService.getCases(id);
}
@GetMapping("/scenario/{id}")
@RequiresPermissions("PROJECT_API_SCENARIO:READ")
public List<TaskCenterDTO> getScenario(@PathVariable String id) {
return taskService.getScenario(id);
}
@PostMapping("/count/running")
@RequiresPermissions("PROJECT_API_SCENARIO:READ")
public int getRunningTasks(@RequestBody TaskCenterRequest request) {

View File

@ -29,4 +29,11 @@ public class TaskService {
return extTaskMapper.getRunningTasks(request);
}
public List<TaskCenterDTO> getCases(String id) {
return extTaskMapper.getCases(id);
}
public List<TaskCenterDTO> getScenario(String id) {
return extTaskMapper.getScenario(id);
}
}

View File

@ -180,6 +180,9 @@
<el-tooltip :content="$t('api_test.scenario.enable')" placement="top" effect="light" v-else>
<font-awesome-icon class="ms-open-btn" :icon="['fas', 'toggle-on']" v-prevent-re-click @click="disableAll"/>
</el-tooltip>
<el-link style="float:right;margin-right: 20px;margin-top: 3px" type="primary" @click.stop @click="showHistory">
{{ $t("commons.debug_history") }}
</el-link>
<div class="ms-debug-result" v-if="debug">
<span class="ms-message-right"> {{ reqTotalTime }} ms </span>
<span class="ms-message-right">{{ $t('api_test.automation.request_total') }} {{ reqTotal }}</span>
@ -276,6 +279,8 @@
<ms-change-history ref="changeHistory"/>
</div>
<ms-task-center ref="taskCenter"/>
</el-card>
</template>
@ -311,6 +316,7 @@ import MsDrawer from "../../../common/components/MsDrawer";
import MsSelectTree from "../../../common/select-tree/SelectTree";
import {saveScenario} from "@/business/components/api/automation/api-automation";
import MsChangeHistory from "../../../history/ChangeHistory";
import MsTaskCenter from "../../../task/TaskCenter";
let jsonPath = require('jsonpath');
export default {
@ -339,7 +345,8 @@ export default {
ScenarioHeader,
MsDrawer,
MsSelectTree,
MsChangeHistory
MsChangeHistory,
MsTaskCenter
},
data() {
return {
@ -1294,6 +1301,9 @@ export default {
this.isTop = false;
}
}
},
showHistory(){
this.$refs.taskCenter.openScenarioHistory(this.currentScenario.id);
}
}
}

View File

@ -31,7 +31,7 @@
<i class="el-icon-edit" style="cursor:pointer" @click="showInput(apiCase)"/>
</span>
<el-link type="primary" style="margin-left: 10px" @click="openHis(apiCase)" v-if="apiCase.id">{{$t('operating_log.change_history')}}</el-link>
<el-link type="primary" style="margin-left: 10px" @click="openHis(apiCase)" v-if="apiCase.id">{{ $t('operating_log.change_history') }}</el-link>
</span>
<div v-if="apiCase.id" style="color: #999999;font-size: 12px">
<!--<span>-->
@ -51,20 +51,20 @@
{{ apiCase.request.method }}
</el-tag>
<el-tooltip :content="apiCase.request.path">
<span class="ms-col-name">{{apiCase.request.path}}</span>
<span class="ms-col-name">{{ apiCase.request.path }}</span>
</el-tooltip>
</span>
</el-col>
<el-col :span="4">
<el-col :span="3">
<div class="tag-item" @click.stop>
<ms-input-tag :currentScenario="apiCase" ref="tag" @keyup.enter.native="saveTestCase(apiCase,true)"/>
</div>
</el-col>
<el-col :span="4">
<el-col :span="3">
<span @click.stop>
<ms-tip-button @click="singleRun(apiCase)" :tip="$t('api_test.run')" icon="el-icon-video-play"
class="run-button" size="mini" :disabled="!apiCase.id" circle/>
class="run-button" size="mini" :disabled="!apiCase.id" circle/>
<ms-tip-button @click="copyCase(apiCase)" :tip="$t('commons.copy')" icon="el-icon-document-copy"
size="mini" :disabled="!apiCase.id || isCaseEdit" circle/>
<ms-tip-button @click="deleteCase(index,apiCase)" :tip="$t('commons.delete')" icon="el-icon-delete"
@ -81,12 +81,16 @@
{{ getResult(apiCase.execResult) }}
</el-link>
<div v-else> {{ getResult(apiCase.execResult) }}</div>
<div v-if="apiCase.id" style="color: #999999;font-size: 12px">
<span> {{ apiCase.execTime | timestampFormatDate }}</span>
{{ apiCase.updateUser }}
</div>
</el-col>
<el-col :span="2">
<el-link style="float:right;" type="primary" @click.stop @click="showHistory(apiCase.id)">
{{ $t("commons.execute_history") }}
</el-link>
</el-col>
</el-row>
</div>
@ -102,7 +106,7 @@
<ms-dubbo-basis-parameters :showScript="true" :request="apiCase.request" v-if="api.protocol==='DUBBO'"/>
<!-- HTTP 请求返回数据 -->
<p class="tip">{{$t('api_test.definition.request.res_param')}}</p>
<p class="tip">{{ $t('api_test.definition.request.res_param') }}</p>
<div v-if="showXpackCompnent&&api.method==='ESB'">
<esb-definition-response v-xpack v-if="showXpackCompnent" :currentProtocol="apiCase.request.protocol" :request="apiCase.request" :is-api-component="false" :show-options-button="false" :show-header="true" :api-item="apiCase"/>
</div>
@ -119,364 +123,366 @@
</div>
</el-collapse-transition>
<ms-change-history ref="changeHistory"/>
</el-card>
</template>
<script>
import {_getBodyUploadFiles, getCurrentProjectID, getUUID} from "@/common/js/utils";
import {PRIORITY} from "../../model/JsonData";
import MsTag from "../../../../common/components/MsTag";
import MsTipButton from "../../../../common/components/MsTipButton";
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import ApiEnvironmentConfig from "../environment/ApiEnvironmentConfig";
import MsApiAssertions from "../assertion/ApiAssertions";
import MsSqlBasisParameters from "../request/database/BasisParameters";
import TcpFormatParameters from "@/business/components/api/definition/components/request/tcp/TcpFormatParameters";
import MsDubboBasisParameters from "../request/dubbo/BasisParameters";
import MsApiExtendBtns from "../reference/ApiExtendBtns";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsRequestResultTail from "../response/RequestResultTail";
import MsJmxStep from "../step/JmxStep";
import ApiResponseComponent from "../../../automation/scenario/component/ApiResponseComponent";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import {_getBodyUploadFiles, getCurrentProjectID, getUUID} from "@/common/js/utils";
import {PRIORITY} from "../../model/JsonData";
import MsTag from "../../../../common/components/MsTag";
import MsTipButton from "../../../../common/components/MsTipButton";
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import ApiEnvironmentConfig from "../environment/ApiEnvironmentConfig";
import MsApiAssertions from "../assertion/ApiAssertions";
import MsSqlBasisParameters from "../request/database/BasisParameters";
import TcpFormatParameters from "@/business/components/api/definition/components/request/tcp/TcpFormatParameters";
import MsDubboBasisParameters from "../request/dubbo/BasisParameters";
import MsApiExtendBtns from "../reference/ApiExtendBtns";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsRequestResultTail from "../response/RequestResultTail";
import MsJmxStep from "../step/JmxStep";
import ApiResponseComponent from "../../../automation/scenario/component/ApiResponseComponent";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const esbDefinition = (requireComponent != null && requireComponent.keys().length) > 0 ? requireComponent("./apidefinition/EsbDefinition.vue") : {};
const esbDefinitionResponse = (requireComponent != null && requireComponent.keys().length) > 0 ? requireComponent("./apidefinition/EsbDefinitionResponse.vue") : {};
import {API_METHOD_COLOUR} from "../../model/JsonData";
import MsChangeHistory from "../../../../history/ChangeHistory";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const esbDefinition = (requireComponent != null && requireComponent.keys().length) > 0 ? requireComponent("./apidefinition/EsbDefinition.vue") : {};
const esbDefinitionResponse = (requireComponent != null && requireComponent.keys().length) > 0 ? requireComponent("./apidefinition/EsbDefinitionResponse.vue") : {};
import {API_METHOD_COLOUR} from "../../model/JsonData";
import MsChangeHistory from "../../../../history/ChangeHistory";
export default {
name: "ApiCaseItem",
components: {
ApiResponseComponent,
MsInputTag,
MsTag,
MsTipButton,
MsApiRequestForm,
ApiEnvironmentConfig,
MsApiAssertions,
MsSqlBasisParameters,
TcpFormatParameters,
MsDubboBasisParameters,
MsApiExtendBtns,
MsRequestResultTail,
MsJmxStep,
ShowMoreBtn,
MsChangeHistory,
"esbDefinition": esbDefinition.default,
"esbDefinitionResponse": esbDefinitionResponse.default
},
data() {
return {
result: {},
grades: [],
resultMap:new Map([
['success', this.$t('test_track.plan_view.execute_result')+''+this.$t('test_track.plan_view.pass')],
['error', this.$t('test_track.plan_view.execute_result')+''+this.$t('api_test.home_page.detail_card.execution_failed')],
['default', this.$t('test_track.plan_view.execute_result')+''+this.$t('api_test.home_page.detail_card.unexecute')]
]),
showXpackCompnent: false,
isReadOnly: false,
selectedEvent: Object,
priorities: PRIORITY,
runData: [],
reportId: "",
checkedCases: new Set(),
visible: false,
condition: {},
responseData: {type: 'HTTP', responseResult: {}, subRequestResults: []},
isShowInput: false,
buttons: [
{name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleRunBatch},
{name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleEditBatch}
],
methodColorMap: new Map(API_METHOD_COLOUR),
}
},
props: {
runResult:{},
selectSize: Number,
apiCase: {
type: Object,
default() {
return {}
},
},
environment: String,
index: {
type: Number,
default() {
return 0
}
},
api: {
type: Object,
default() {
return {}
}
},
type: String,
isCaseEdit: Boolean,
},
created() {
if (requireComponent != null && JSON.stringify(esbDefinition) != '{}' && JSON.stringify(esbDefinitionResponse) != '{}') {
this.showXpackCompnent = true;
}
},
watch: {
'apiCase.selected'(){
this.$emit('apiCaseSelected');
}
},
methods: {
openHis(row) {
this.$refs.changeHistory.open(row.id);
},
handleRunBatch() {
this.$emit('batchRun');
},
getColor(enable, method) {
if (enable) {
return this.methodColorMap.get(method);
}
},
handleEditBatch() {
this.$emit('batchEditCase');
},
deleteCase(index, row) {
this.$alert(this.$t('api_test.definition.request.delete_case_confirm') + ' ' + row.name + " ", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.$get('/api/testcase/delete/' + row.id, () => {
this.$success(this.$t('commons.delete_success'));
this.$emit('refresh');
});
}
}
});
},
singleRun(data) {
if (this.api.protocol !== "SQL" && this.api.protocol != "DUBBO" && this.api.protocol != "dubbo://" && !this.environment) {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}
data.message = true;
data.request.useEnvironment = this.environment;
this.saveTestCase(data);
this.$emit('singleRun', data);
},
copyCase(data) {
if (data && data.request) {
let uuid = getUUID();
let request = JSON.parse(JSON.stringify(data.request));
request.id = uuid;
let obj = {name: "copy_" + data.name, priority: data.priority, active: true, tags: data.tags, request: request, uuid: uuid};
this.$emit('copyCase', obj);
}
},
selectTestCase(item, $event) {
if (!item.id || !this.loaded) {
return;
}
if ($event.currentTarget.className.indexOf('is-selected') > 0) {
$event.currentTarget.className = "el-card is-always-shadow";
this.$emit('selectTestCase', null);
} else {
if (this.selectedEvent.currentTarget != undefined) {
this.selectedEvent.currentTarget.className = "el-card is-always-shadow";
}
this.selectedEvent.currentTarget = $event.currentTarget;
$event.currentTarget.className = "el-card is-always-shadow is-selected";
this.$emit('selectTestCase', item);
}
},
changePriority(row) {
if (row.id) {
this.saveTestCase(row);
}
},
setParameters(data) {
data.projectId = getCurrentProjectID();
data.request.name = data.name;
if (data.protocol === "DUBBO" || data.protocol === "dubbo://") {
data.request.protocol = "dubbo://";
} else {
data.request.protocol = data.protocol;
}
data.id = data.request.id;
if (!data.method) {
data.method = data.protocol;
}
},
addModule(row) {
this.saveApi(row, "default-module");
},
saveApi(row, module) {
let data = this.api;
data.name = this.apiCase.name;
data.moduleId = module;
data.modulePath = "/" + this.$t('commons.module_title');
this.setParameters(data);
let bodyFiles = this.getBodyUploadFiles(data);
this.$fileUpload("/api/definition/create", null, bodyFiles, data, () => {
if (row) {
this.api.saved = false;
row.apiDefinitionId = data.id;
this.saveCase(row);
}
});
},
saveCase(row, hideAlert) {
let tmp = JSON.parse(JSON.stringify(row));
this.isShowInput = false;
if (this.validate(tmp)) {
return;
}
tmp.request.body = row.request.body;
let bodyFiles = this.getBodyUploadFiles(tmp);
tmp.projectId = getCurrentProjectID();
tmp.active = true;
tmp.apiDefinitionId = tmp.apiDefinitionId || this.api.id;
let url = "/api/testcase/create";
if (tmp.id) {
url = "/api/testcase/update";
} else {
tmp.id = tmp.request.id;
tmp.request.path = this.api.path;
if (tmp.request.protocol != "dubbo://" && tmp.request.protocol != "DUBBO") {
tmp.request.method = this.api.method;
}
}
if (tmp.request.esbDataStruct != null) {
tmp.esbDataStruct = JSON.stringify(tmp.request.esbDataStruct);
}
if (tmp.request.backEsbDataStruct != null) {
tmp.backEsbDataStruct = JSON.stringify(tmp.request.backEsbDataStruct);
}
if (tmp.tags instanceof Array) {
tmp.tags = JSON.stringify(tmp.tags);
}
this.result = this.$fileUpload(url, null, bodyFiles, tmp, (response) => {
let data = response.data;
row.id = data.id;
row.createTime = data.createTime;
row.updateTime = data.updateTime;
if (!row.message) {
this.$success(this.$t('commons.save_success'));
if (!hideAlert) {
this.$emit('refresh');
}
}
});
},
saveTestCase(row, hideAlert) {
if (this.api.saved) {
this.addModule(row);
} else {
this.saveCase(row, hideAlert);
}
},
showInput(row) {
// row.type = "create";
this.isShowInput = true;
row.active = true;
this.active(row);
this.$nextTick(() => {
this.$refs.nameEdit.focus();
});
},
active(item) {
item.active = !item.active;
},
getResult(data) {
if (this.resultMap.get(data)) {
return this.resultMap.get(data);
} else {
return this.resultMap.get("default");
}
},
validate(row) {
if (!row.name) {
this.$warning(this.$t('api_test.input_name'));
return true;
}
},
showExecResult(item) {
item.active = true;
item.isActive = true;
},
getBodyUploadFiles(row) {
let bodyUploadFiles = [];
row.bodyUploadIds = [];
_getBodyUploadFiles(row.request, bodyUploadFiles, row);
return bodyUploadFiles;
},
export default {
name: "ApiCaseItem",
components: {
ApiResponseComponent,
MsInputTag,
MsTag,
MsTipButton,
MsApiRequestForm,
ApiEnvironmentConfig,
MsApiAssertions,
MsSqlBasisParameters,
TcpFormatParameters,
MsDubboBasisParameters,
MsApiExtendBtns,
MsRequestResultTail,
MsJmxStep,
ShowMoreBtn,
MsChangeHistory,
"esbDefinition": esbDefinition.default,
"esbDefinitionResponse": esbDefinitionResponse.default
},
data() {
return {
result: {},
grades: [],
resultMap: new Map([
['success', this.$t('test_track.plan_view.execute_result') + '' + this.$t('test_track.plan_view.pass')],
['error', this.$t('test_track.plan_view.execute_result') + '' + this.$t('api_test.home_page.detail_card.execution_failed')],
['default', this.$t('test_track.plan_view.execute_result') + '' + this.$t('api_test.home_page.detail_card.unexecute')]
]),
showXpackCompnent: false,
isReadOnly: false,
selectedEvent: Object,
priorities: PRIORITY,
runData: [],
reportId: "",
checkedCases: new Set(),
visible: false,
condition: {},
responseData: {type: 'HTTP', responseResult: {}, subRequestResults: []},
isShowInput: false,
buttons: [
{name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleRunBatch},
{name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleEditBatch}
],
methodColorMap: new Map(API_METHOD_COLOUR),
}
},
props: {
runResult: {},
selectSize: Number,
apiCase: {
type: Object,
default() {
return {}
},
},
environment: String,
index: {
type: Number,
default() {
return 0
}
},
api: {
type: Object,
default() {
return {}
}
},
type: String,
isCaseEdit: Boolean,
},
created() {
if (requireComponent != null && JSON.stringify(esbDefinition) != '{}' && JSON.stringify(esbDefinitionResponse) != '{}') {
this.showXpackCompnent = true;
}
},
watch: {
'apiCase.selected'() {
this.$emit('apiCaseSelected');
}
},
methods: {
openHis(row) {
this.$refs.changeHistory.open(row.id);
},
handleRunBatch() {
this.$emit('batchRun');
},
getColor(enable, method) {
if (enable) {
return this.methodColorMap.get(method);
}
},
handleEditBatch() {
this.$emit('batchEditCase');
},
deleteCase(index, row) {
this.$alert(this.$t('api_test.definition.request.delete_case_confirm') + ' ' + row.name + " ", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.$get('/api/testcase/delete/' + row.id, () => {
this.$success(this.$t('commons.delete_success'));
this.$emit('refresh');
});
}
}
});
},
singleRun(data) {
if (this.api.protocol !== "SQL" && this.api.protocol != "DUBBO" && this.api.protocol != "dubbo://" && !this.environment) {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}
data.message = true;
data.request.useEnvironment = this.environment;
this.saveTestCase(data);
this.$emit('singleRun', data);
},
copyCase(data) {
if (data && data.request) {
let uuid = getUUID();
let request = JSON.parse(JSON.stringify(data.request));
request.id = uuid;
let obj = {name: "copy_" + data.name, priority: data.priority, active: true, tags: data.tags, request: request, uuid: uuid};
this.$emit('copyCase', obj);
}
},
selectTestCase(item, $event) {
if (!item.id || !this.loaded) {
return;
}
if ($event.currentTarget.className.indexOf('is-selected') > 0) {
$event.currentTarget.className = "el-card is-always-shadow";
this.$emit('selectTestCase', null);
} else {
if (this.selectedEvent.currentTarget != undefined) {
this.selectedEvent.currentTarget.className = "el-card is-always-shadow";
}
this.selectedEvent.currentTarget = $event.currentTarget;
$event.currentTarget.className = "el-card is-always-shadow is-selected";
this.$emit('selectTestCase', item);
}
},
changePriority(row) {
if (row.id) {
this.saveTestCase(row);
}
},
setParameters(data) {
data.projectId = getCurrentProjectID();
data.request.name = data.name;
if (data.protocol === "DUBBO" || data.protocol === "dubbo://") {
data.request.protocol = "dubbo://";
} else {
data.request.protocol = data.protocol;
}
data.id = data.request.id;
if (!data.method) {
data.method = data.protocol;
}
},
addModule(row) {
this.saveApi(row, "default-module");
},
saveApi(row, module) {
let data = this.api;
data.name = this.apiCase.name;
data.moduleId = module;
data.modulePath = "/" + this.$t('commons.module_title');
this.setParameters(data);
let bodyFiles = this.getBodyUploadFiles(data);
this.$fileUpload("/api/definition/create", null, bodyFiles, data, () => {
if (row) {
this.api.saved = false;
row.apiDefinitionId = data.id;
this.saveCase(row);
}
});
},
saveCase(row, hideAlert) {
let tmp = JSON.parse(JSON.stringify(row));
this.isShowInput = false;
if (this.validate(tmp)) {
return;
}
tmp.request.body = row.request.body;
let bodyFiles = this.getBodyUploadFiles(tmp);
tmp.projectId = getCurrentProjectID();
tmp.active = true;
tmp.apiDefinitionId = tmp.apiDefinitionId || this.api.id;
let url = "/api/testcase/create";
if (tmp.id) {
url = "/api/testcase/update";
} else {
tmp.id = tmp.request.id;
tmp.request.path = this.api.path;
if (tmp.request.protocol != "dubbo://" && tmp.request.protocol != "DUBBO") {
tmp.request.method = this.api.method;
}
}
if (tmp.request.esbDataStruct != null) {
tmp.esbDataStruct = JSON.stringify(tmp.request.esbDataStruct);
}
if (tmp.request.backEsbDataStruct != null) {
tmp.backEsbDataStruct = JSON.stringify(tmp.request.backEsbDataStruct);
}
if (tmp.tags instanceof Array) {
tmp.tags = JSON.stringify(tmp.tags);
}
this.result = this.$fileUpload(url, null, bodyFiles, tmp, (response) => {
let data = response.data;
row.id = data.id;
row.createTime = data.createTime;
row.updateTime = data.updateTime;
if (!row.message) {
this.$success(this.$t('commons.save_success'));
if (!hideAlert) {
this.$emit('refresh');
}
}
});
},
saveTestCase(row, hideAlert) {
if (this.api.saved) {
this.addModule(row);
} else {
this.saveCase(row, hideAlert);
}
},
showInput(row) {
// row.type = "create";
this.isShowInput = true;
row.active = true;
this.active(row);
this.$nextTick(() => {
this.$refs.nameEdit.focus();
});
},
active(item) {
item.active = !item.active;
},
getResult(data) {
if (this.resultMap.get(data)) {
return this.resultMap.get(data);
} else {
return this.resultMap.get("default");
}
},
validate(row) {
if (!row.name) {
this.$warning(this.$t('api_test.input_name'));
return true;
}
},
showExecResult(item) {
item.active = true;
item.isActive = true;
},
getBodyUploadFiles(row) {
let bodyUploadFiles = [];
row.bodyUploadIds = [];
_getBodyUploadFiles(row.request, bodyUploadFiles, row);
return bodyUploadFiles;
},
showHistory(id) {
this.$emit("showHistory", id);
},
}
}
</script>
<style scoped>
.ms-api-select {
margin-left: 10px;
width: 65px;
}
.ms-api-select {
margin-left: 10px;
width: 65px;
}
.ms-api-header-select {
margin-left: 20px;
min-width: 100px;
}
.ms-api-header-select {
margin-left: 20px;
min-width: 100px;
}
.ms-api-label {
color: #CCCCCC;
}
.ms-api-label {
color: #CCCCCC;
}
.ms-api-col {
background-color: #7C3985;
border-color: #7C3985;
margin-right: 10px;
color: white;
}
.ms-api-col {
background-color: #7C3985;
border-color: #7C3985;
margin-right: 10px;
color: white;
}
.is-selected {
background: #EFF7FF;
}
.is-selected {
background: #EFF7FF;
}
.icon.is-active {
transform: rotate(90deg);
}
.icon.is-active {
transform: rotate(90deg);
}
.item-select {
margin-right: 10px;
}
.item-select {
margin-right: 10px;
}
.ms-opt-btn {
position: fixed;
left: 60px;
z-index: 1;
}
.ms-opt-btn {
position: fixed;
left: 60px;
z-index: 1;
}
.api-el-tag {
color: white;
}
.api-el-tag {
color: white;
}
.tag-item {
margin-right: 20px;
}
.tag-item {
margin-right: 20px;
}
.ms-col-name {
display: inline-block;
margin: 0 5px;
overflow-x: hidden;
padding-bottom: 0;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
width: 150px;
}
.ms-col-name {
display: inline-block;
margin: 0 5px;
overflow-x: hidden;
padding-bottom: 0;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
width: 150px;
}
</style>

View File

@ -31,6 +31,7 @@
@batchEditCase="batchEditCase"
@batchRun="batchRun"
@apiCaseSelected="apiCaseSelected"
@showHistory="showHistory"
:environment="environment"
:select-size="selectSize"
:is-case-edit="isCaseEdit"
@ -47,6 +48,9 @@
@runRefresh="runRefresh" @errorRefresh="errorRefresh" ref="runTest"/>
<!--批量编辑-->
<ms-batch-edit ref="batchEdit" @batchEdit="batchEdit" :typeArr="typeArr" :data-count="selectdCases.length" :value-arr="valueArr"/>
<ms-task-center ref="taskCenter"/>
</div>
</template>
<script>
@ -59,6 +63,7 @@ import MsDrawer from "../../../../common/components/MsDrawer";
import {CASE_ORDER, CASE_PRIORITY, DUBBO_METHOD, REQ_METHOD, SQL_METHOD, TCP_METHOD} from "../../model/JsonData";
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
import MsBatchEdit from "../basis/BatchEdit";
import MsTaskCenter from "../../../../task/TaskCenter";
export default {
name: 'ApiCaseList',
@ -67,7 +72,8 @@ export default {
MsRun,
ApiCaseHeader,
ApiCaseItem,
MsBatchEdit
MsBatchEdit,
MsTaskCenter,
},
props: {
createCase: String,
@ -470,6 +476,9 @@ export default {
this.getApiTest();
});
},
showHistory(id){
this.$refs.taskCenter.openHistory(id);
}
}
};
</script>

View File

@ -88,10 +88,18 @@
:label="$t('test_track.plan_view.execute_result')">
<template v-slot:default="scope">
<i class="el-icon-loading ms-running" v-if="scope.row.status === 'Running'"/>
<span :class="getStatusClass(scope.row.status)">{{ getStatusTitle(scope.row.status) }}</span>
<el-link @click="getExecResult(scope.row)" :class="getStatusClass(scope.row.status)">{{ getStatusTitle(scope.row.status) }}</el-link>
</template>
</ms-table-column>
<ms-table-column
prop="passRate"
:field="item"
:fields-width="fieldsWidth"
min-width="100px"
:label="$t('commons.pass_rate')">
</ms-table-column>
<ms-table-column
sortable="custom"
prop="path"
@ -166,6 +174,10 @@
<api-case-batch-run :project-id="projectId" @batchRun="runBatch" ref="batchRun"/>
<el-dialog :close-on-click-modal="false" :title="$t('test_track.plan_view.test_result')" width="60%"
:visible.sync="resVisible" class="api-import" destroy-on-close @close="resVisible=false">
<ms-request-result-tail :response="response" ref="debugResult"/>
</el-dialog>
</div>
</template>
@ -205,8 +217,8 @@ import {
} from "@/common/js/tableUtils";
import {API_CASE_LIST} from "@/common/js/constants";
import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate";
import {apiCaseBatchRun} from "@/network/api";
import ApiCaseBatchRun from "@/business/components/api/definition/components/list/ApiCaseBatchRun";
import MsRequestResultTail from "../../../../api/definition/components/response/RequestResultTail";
export default {
name: "ApiCaseSimpleList",
@ -230,7 +242,8 @@ export default {
MsReferenceView,
MsTableAdvSearchBar,
MsTable,
MsTableColumn
MsTableColumn,
MsRequestResultTail
},
data() {
return {
@ -326,6 +339,8 @@ export default {
unSelection: [],
selectDataCounts: 0,
environments: [],
resVisible: false,
response: {},
}
},
props: {
@ -419,6 +434,18 @@ export default {
}
},
methods: {
getExecResult(apiCase) {
if (apiCase.lastResultId) {
let url = "/api/definition/report/get/" + apiCase.lastResultId;
this.$get(url, response => {
if (response.data) {
let data = JSON.parse(response.data.content);
this.response = data;
this.resVisible = true;
}
});
}
},
getStatusClass(status) {
switch (status) {
case "success":

View File

@ -10,7 +10,7 @@
<template v-slot:content>
<span>{{ $t('commons.task_center') }}</span>
</template>
<div @click="showTaskCenter" v-if="runningTotal > 0" >
<div @click="showTaskCenter" v-if="runningTotal > 0">
<el-badge :value="runningTotal" class="item" type="primary">
<font-awesome-icon class="icon global focusing" :icon="['fas', 'tasks']"
style="font-size: 18px"/>
@ -118,6 +118,7 @@ export default {
response: {},
initEnd: false,
visible: false,
showType: "",
runMode: [
{id: '', label: this.$t('api_test.definition.document.data_set.all')},
{id: 'BATCH', label: this.$t('api_test.automation.batch_execute')},
@ -137,7 +138,7 @@ export default {
],
condition: {triggerMode: "", executionStatus: ""},
maintainerOptions: [],
websocket:Object,
websocket: Object,
};
},
props: {
@ -149,13 +150,13 @@ export default {
}
},
watch: {
taskVisible(v){
if(!v){
taskVisible(v) {
if (!v) {
this.close();
}
}
},
methods: {
methods: {
format(item) {
return '';
},
@ -203,7 +204,10 @@ export default {
close() {
this.visible = false;
this.taskVisible = false;
this.websocket.close();
this.showType = "";
if (this.websocket && this.websocket.close instanceof Function) {
this.websocket.close();
}
},
open() {
this.showTaskCenter();
@ -303,6 +307,9 @@ export default {
}
},
init() {
if (this.showType === "CASE" || this.showType === "SCENARIO") {
return;
}
this.result.loading = true;
this.condition.projectId = getCurrentProjectID();
this.result = this.$post('/task/center/list', this.condition, response => {
@ -310,6 +317,23 @@ export default {
this.calculationRunningTotal();
this.initEnd = true;
});
},
initCaseHistory(id) {
this.result = this.$get('/task/center/case/' + id, response => {
this.taskData = response.data;
});
},
openHistory(id) {
this.initCaseHistory(id);
this.taskVisible = true;
this.showType = "CASE";
},
openScenarioHistory(id) {
this.result = this.$get('/task/center/scenario/' + id, response => {
this.taskData = response.data;
});
this.showType = "SCENARIO";
this.taskVisible = true;
}
}
};

@ -1 +1 @@
Subproject commit 34430c98ba5cbfefa6d911ef9be9f593a2368f69
Subproject commit 34379f3a7e7d52b4a9c0194325b3ea02db5cc8eb

View File

@ -55,10 +55,11 @@ export let CUSTOM_TABLE_HEADER = {
{id: 'path', key: '4', label: 'api_test.definition.api_definition_path'},
{id: 'status', key: '5', label: 'test_track.plan_view.execute_result'},
{id: 'casePath', key: '6', label: 'api_test.definition.api_case_path'},
{id: 'tags', key: '7', label: 'commons.tag'},
{id: 'createUser', key: '8', label: 'api_test.creator'},
{id: 'updateTime', key: '9', label: 'api_test.definition.api_last_time'},
{id: 'createTime', key: '10', label: 'commons.create_time'},
{id: 'passRate', key: '7', label: 'commons.pass_rate'},
{id: 'tags', key: '8', label: 'commons.tag'},
{id: 'createUser', key: '9', label: 'api_test.creator'},
{id: 'updateTime', key: '10', label: 'api_test.definition.api_last_time'},
{id: 'createTime', key: '11', label: 'commons.create_time'},
],
//场景测试
API_SCENARIO: [

View File

@ -168,6 +168,8 @@ export default {
create_user: 'Creator',
run_message: "The task is being executed, please go to the task center to view the details",
executor: "Executor",
execute_history: "Execute history",
debug_history: "Debug history",
table: {
select_tip: "Item {0} data is selected"
},

View File

@ -169,6 +169,8 @@ export default {
create_user: '创建人',
run_message: "任务执行中,请到任务中心查看详情",
executor: "执行人",
execute_history: "执行历史",
debug_history: "调试历史",
table: {
select_tip: "已选中 {0} 条数据"
},

View File

@ -169,6 +169,8 @@ export default {
create_user: "創建人",
run_message: "任務執行中,請到任務中心查看詳情",
executor: "執行人",
execute_history: "執行歷史",
debug_history: "調試歷史",
selector: {
required: "必填",
not_required: "非必填",