This commit is contained in:
chenjianxing 2021-01-27 20:07:29 +08:00
commit 0fb4fdb8ae
36 changed files with 528 additions and 260 deletions

View File

@ -158,7 +158,8 @@ public class MsHTTPSamplerProxy extends MsTestElement {
if (!path.startsWith("/")) { if (!path.startsWith("/")) {
path = "/" + path; path = "/" + path;
} }
path = sampler.getProtocol() + "://" + sampler.getDomain() + ":" + sampler.getPort() + path; String port = sampler.getPort() != 80 ? ":" + sampler.getPort() : "";
path = sampler.getProtocol() + "://" + sampler.getDomain() + port + path;
} }
sampler.setPath(path); sampler.setPath(path);
} }

View File

@ -24,7 +24,6 @@ import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -51,7 +50,7 @@ public class MsJDBCSampler extends MsTestElement {
@JSONField(ordinal = 28) @JSONField(ordinal = 28)
private String dataSourceId; private String dataSourceId;
@JSONField(ordinal = 29) @JSONField(ordinal = 29)
private String protocol="SQL"; private String protocol = "SQL";
@Override @Override
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) { public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
@ -62,7 +61,8 @@ public class MsJDBCSampler extends MsTestElement {
this.getRefElement(this); this.getRefElement(this);
} }
if (StringUtils.isNotEmpty(dataSourceId)) { if (StringUtils.isNotEmpty(dataSourceId)) {
initDataSource(); this.dataSource = null;
this.initDataSource();
} }
if (this.dataSource == null) { if (this.dataSource == null) {
MSException.throwException("数据源为空无法执行"); MSException.throwException("数据源为空无法执行");
@ -79,14 +79,16 @@ public class MsJDBCSampler extends MsTestElement {
private void initDataSource() { private void initDataSource() {
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
ApiTestEnvironmentWithBLOBs environment = environmentService.get(this.dataSourceId); ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentId);
if (environment != null && environment.getConfig() != null) { if (environment != null && environment.getConfig() != null) {
EnvironmentConfig config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); EnvironmentConfig envConfig = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
if (CollectionUtils.isNotEmpty(config.getDatabaseConfigs())) { if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) {
List<DatabaseConfig> databaseConfigs = config.getDatabaseConfigs().stream().filter((DatabaseConfig d) -> this.dataSourceId.equals(d.getId())).collect(Collectors.toList()); envConfig.getDatabaseConfigs().forEach(item -> {
if (CollectionUtils.isNotEmpty(databaseConfigs)) { if (item.getId().equals(this.dataSourceId)) {
this.dataSource = databaseConfigs.get(0); this.dataSource = item;
} return;
}
});
} }
} }
} }

View File

@ -297,6 +297,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
requestResult.setHeaders(result.getRequestHeaders()); requestResult.setHeaders(result.getRequestHeaders());
requestResult.setRequestSize(result.getSentBytes()); requestResult.setRequestSize(result.getSentBytes());
requestResult.setStartTime(result.getStartTime()); requestResult.setStartTime(result.getStartTime());
requestResult.setEndTime(result.getEndTime());
requestResult.setTotalAssertions(result.getAssertionResults().length); requestResult.setTotalAssertions(result.getAssertionResults().length);
requestResult.setSuccess(result.isSuccessful()); requestResult.setSuccess(result.isSuccessful());
requestResult.setError(result.getErrorCount()); requestResult.setError(result.getErrorCount());

View File

@ -18,6 +18,8 @@ public class RequestResult {
private long startTime; private long startTime;
private long endTime;
private int error; private int error;
private boolean success; private boolean success;

View File

@ -323,7 +323,9 @@ public class ApiDefinitionService {
apiDefinitionMapper.updateByPrimaryKeyWithBLOBs(apiDefinition); apiDefinitionMapper.updateByPrimaryKeyWithBLOBs(apiDefinition);
} }
} else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) { } else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) {
batchMapper.insert(apiDefinition); if (CollectionUtils.isEmpty(sameRequest)) {
batchMapper.insert(apiDefinition);
}
} else { } else {
if (CollectionUtils.isEmpty(sameRequest)) { if (CollectionUtils.isEmpty(sameRequest)) {
batchMapper.insert(apiDefinition); batchMapper.insert(apiDefinition);

View File

@ -163,6 +163,7 @@ public class ApiScenarioReportService {
String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError())); String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError()));
testPlanApiScenario.setPassRate(passRate); testPlanApiScenario.setPassRate(passRate);
testPlanApiScenario.setReportId(report.getId()); testPlanApiScenario.setReportId(report.getId());
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario); testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
} }
returnReport = report; returnReport = report;
@ -220,6 +221,7 @@ public class ApiScenarioReportService {
apiScenarioReportDetailMapper.insert(detail); apiScenarioReportDetailMapper.insert(detail);
testPlanApiScenario.setReportId(report.getId()); testPlanApiScenario.setReportId(report.getId());
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario); testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
lastReport = report; lastReport = report;
@ -343,13 +345,41 @@ public class ApiScenarioReportService {
ids = allIds.stream().filter(id -> !reportRequest.getUnSelectIds().contains(id)).collect(Collectors.toList()); ids = allIds.stream().filter(id -> !reportRequest.getUnSelectIds().contains(id)).collect(Collectors.toList());
} }
ApiScenarioReportDetailExample detailExample = new ApiScenarioReportDetailExample(); //为预防数量太多调用删除方法时引起SQL过长的Bug此处采取分批执行的方式
detailExample.createCriteria().andReportIdIn(ids); //每次处理的数据数量
apiScenarioReportDetailMapper.deleteByExample(detailExample); int handleCount = 7000;
//每次处理的集合
List<String> handleIdList = new ArrayList<>(handleCount);
while (ids.size() > handleCount){
handleIdList = new ArrayList<>(handleCount);
List<String> otherIdList = new ArrayList<>();
for (int index = 0;index < ids.size();index++){
if(index<handleCount){
handleIdList.add(ids.get(index));
}else{
otherIdList.add(ids.get(index));
}
}
//处理本次的数据
ApiScenarioReportDetailExample detailExample = new ApiScenarioReportDetailExample();
detailExample.createCriteria().andReportIdIn(handleIdList);
apiScenarioReportDetailMapper.deleteByExample(detailExample);
ApiScenarioReportExample apiTestReportExample = new ApiScenarioReportExample();
apiTestReportExample.createCriteria().andIdIn(handleIdList);
apiScenarioReportMapper.deleteByExample(apiTestReportExample);
//转存剩余的数据
ids = otherIdList;
}
ApiScenarioReportExample apiTestReportExample = new ApiScenarioReportExample(); //处理最后剩余的数据
apiTestReportExample.createCriteria().andIdIn(ids); if(!ids.isEmpty()){
apiScenarioReportMapper.deleteByExample(apiTestReportExample); ApiScenarioReportDetailExample detailExample = new ApiScenarioReportDetailExample();
detailExample.createCriteria().andReportIdIn(ids);
apiScenarioReportDetailMapper.deleteByExample(detailExample);
ApiScenarioReportExample apiTestReportExample = new ApiScenarioReportExample();
apiTestReportExample.createCriteria().andIdIn(ids);
apiScenarioReportMapper.deleteByExample(apiTestReportExample);
}
} }
public long countByProjectID(String projectId) { public long countByProjectID(String projectId) {

View File

@ -219,7 +219,8 @@
</if> </if>
<if test="request.name != null"> <if test="request.name != null">
and (api_definition.name like CONCAT('%', #{request.name},'%') and (api_definition.name like CONCAT('%', #{request.name},'%')
or api_definition.tags like CONCAT('%', #{request.name},'%')) or api_definition.tags like CONCAT('%', #{request.name},'%')
or api_definition.num like CONCAT('%', #{request.name},'%'))
</if> </if>
<if test="request.protocol != null"> <if test="request.protocol != null">
AND api_definition.protocol = #{request.protocol} AND api_definition.protocol = #{request.protocol}

View File

@ -143,7 +143,9 @@
</if> </if>
<if test="request.name != null"> <if test="request.name != null">
and (api_scenario.name like CONCAT('%', #{request.name},'%') or api_scenario.tags like CONCAT('%', #{request.name},'%')) and (api_scenario.name like CONCAT('%', #{request.name},'%')
or api_scenario.tags like CONCAT('%', #{request.name},'%')
or api_scenario.num like CONCAT('%', #{request.name},'%'))
</if> </if>
<if test="request.workspaceId != null"> <if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId} AND project.workspace_id = #{request.workspaceId}

View File

@ -284,7 +284,9 @@
</foreach> </foreach>
</if> </if>
<if test="request.name != null and request.name!=''"> <if test="request.name != null and request.name!=''">
and (t1.name like CONCAT('%', #{request.name},'%') or t1.tags like CONCAT('%', #{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>
<if test="request.createTime > 0"> <if test="request.createTime > 0">
and t1.create_time >= #{request.createTime} and t1.create_time >= #{request.createTime}

View File

@ -122,7 +122,7 @@
<select id="list" resultType="io.metersphere.track.dto.TestPlanCaseDTO"> <select id="list" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
select test_plan_test_case.id as id, test_case.id as caseId, test_case.name, test_case.priority, select test_plan_test_case.id as id, test_case.id as caseId, test_case.name, test_case.priority,
test_case.type,test_case.test_id as testId,test_case.node_id, test_case.type,test_case.test_id as testId,test_case.node_id, test_case.tags,
test_case.node_path, test_case.method, test_case.num, test_plan_test_case.executor, test_plan_test_case.status, test_case.node_path, test_case.method, test_case.num, test_plan_test_case.executor, test_plan_test_case.status,
test_plan_test_case.update_time, test_case_node.name as model, project.name as projectName, test_plan_test_case.update_time, test_case_node.name as model, project.name as projectName,
test_plan_test_case.plan_id as planId test_plan_test_case.plan_id as planId
@ -139,7 +139,7 @@
</if> </if>
<if test="request.name != null"> <if test="request.name != null">
and (test_case.name like CONCAT('%', #{request.name},'%') or test_case.num like and (test_case.name like CONCAT('%', #{request.name},'%') or test_case.num like
CONCAT('%',#{request.name},'%')) CONCAT('%',#{request.name},'%') or test_case.tags like CONCAT('%', #{request.name},'%'))
</if> </if>
<if test="request.id != null"> <if test="request.id != null">
and test_case.id = #{request.id} and test_case.id = #{request.id}

@ -1 +1 @@
Subproject commit 387ca56312b62ae5edb3d7f34afa08946d86d621 Subproject commit 26d36f3f81e889f58eed7c6903252a129b301d98

View File

@ -1,11 +1,11 @@
create table swagger_url_project create table swagger_url_project
( (
id varchar(255) not null, id varchar(30) not null,
project_id varchar(255) null, project_id varchar(30) null,
swagger_url varchar(255) null, swagger_url varchar(255) null,
module_id varchar(255) null, module_id varchar(30) null,
module_path varchar(255) null, module_path varchar(255) null,
mode_id varchar(255) null, mode_id varchar(30) null,
primary key (id) primary key (id)
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4; DEFAULT CHARSET = utf8mb4;

View File

@ -14,7 +14,7 @@
<ms-main-container> <ms-main-container>
<el-tabs v-model="activeName" @tab-click="addTab" @tab-remove="removeTab"> <el-tabs v-model="activeName" @tab-click="addTab" @tab-remove="removeTab">
<el-tab-pane name="default" :label="$t('api_test.automation.scenario_test')"> <el-tab-pane name="default" :label="$t('api_test.automation.scenario_list')">
<ms-api-scenario-list <ms-api-scenario-list
:module-tree="nodeTree" :module-tree="nodeTree"
:module-options="moduleOptions" :module-options="moduleOptions"
@ -97,6 +97,7 @@
renderComponent: true, renderComponent: true,
isHide: true, isHide: true,
activeName: 'default', activeName: 'default',
redirectFlag: 'none',
currentModule: null, currentModule: null,
moduleOptions: [], moduleOptions: [],
tabs: [], tabs: [],
@ -150,6 +151,15 @@
}, },
changeRedirectParam(redirectIDParam) { changeRedirectParam(redirectIDParam) {
this.redirectID = redirectIDParam; this.redirectID = redirectIDParam;
if(redirectIDParam!=null){
if(this.redirectFlag == "none"){
this.activeName = "default";
this.addListener();
this.redirectFlag = "redirected";
}
}else{
this.redirectFlag = "none";
}
}, },
addTab(tab) { addTab(tab) {
if (!getCurrentProjectID()) { if (!getCurrentProjectID()) {
@ -227,8 +237,18 @@
this.$refs.apiScenarioList.search(data); this.$refs.apiScenarioList.search(data);
}, },
refresh(data) { refresh(data) {
this.setTabTitle(data);
this.$refs.apiScenarioList.search(data); this.$refs.apiScenarioList.search(data);
}, },
setTabTitle(data) {
for (let index in this.tabs) {
let tab = this.tabs[index];
if (tab.name === this.activeName) {
tab.label = data.name;
break;
}
}
},
editScenario(row) { editScenario(row) {
this.addTab({name: 'edit', currentScenario: row}); this.addTab({name: 'edit', currentScenario: row});
}, },

View File

@ -118,6 +118,7 @@
throw e; throw e;
} }
this.getFails(); this.getFails();
this.computeTotalTime();
this.loading = false; this.loading = false;
} else { } else {
setTimeout(this.getReport, 2000) setTimeout(this.getReport, 2000)
@ -146,12 +147,30 @@
failScenario.requestResults.push(failRequest); failScenario.requestResults.push(failRequest);
} }
}) })
} }
}) })
} }
} }
}, },
computeTotalTime() {
if (this.content.scenarios) {
let startTime = 99991611737506593;
let endTime = 0;
this.content.scenarios.forEach((scenario) => {
scenario.requestResults.forEach((request) => {
if (request.startTime && Number(request.startTime) < startTime) {
startTime = request.startTime;
}
if (request.endTime && Number(request.endTime) > endTime) {
endTime = request.endTime;
}
})
})
if (startTime < endTime) {
this.totalTime = endTime - startTime + 100;
}
}
},
requestResult(requestResult) { requestResult(requestResult) {
this.active(); this.active();
this.isRequestResult = false; this.isRequestResult = false;

View File

@ -1,17 +1,21 @@
<template> <template>
<div class="request-result"> <div class="request-result">
<div> <div>
<el-row :gutter="10" type="flex" align="middle" class="info"> <el-row :gutter="8" type="flex" align="middle" class="info">
<el-col :span="2"> <el-col :span="2">
<div class="method"> <div class="method">
{{request.method}} {{request.method}}
</div> </div>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="8">
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800"> <el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
<div class="url">{{request.url}}</div> <div class="url">{{request.url}}</div>
</el-tooltip> </el-tooltip>
</el-col> </el-col>
<el-col :span="8">
<div class="url"> {{$t('api_report.start_time')}}{{request.startTime | timestampFormatDate(true) }}
</div>
</el-col>
</el-row> </el-row>
</div> </div>
<el-collapse-transition> <el-collapse-transition>
@ -19,7 +23,7 @@
<el-tabs v-model="activeName" v-show="isActive" v-if="hasSub"> <el-tabs v-model="activeName" v-show="isActive" v-if="hasSub">
<el-tab-pane :label="$t('api_report.sub_result')" name="sub"> <el-tab-pane :label="$t('api_report.sub_result')" name="sub">
<ms-request-sub-result class="sub-result" v-for="(sub, index) in request.subRequestResults" <ms-request-sub-result class="sub-result" v-for="(sub, index) in request.subRequestResults"
:key="index" :indexNumber="index" :request="sub"/> :key="index" :indexNumber="index" :request="sub"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('api_report.request_result')" name="result"> <el-tab-pane :label="$t('api_report.request_result')" name="result">
<ms-response-text :request-type="requestType" :response="request.responseResult" :request="request"/> <ms-response-text :request-type="requestType" :response="request.responseResult" :request="request"/>
@ -43,7 +47,7 @@
export default { export default {
name: "MsRequestResultTail", name: "MsRequestResultTail",
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult,MsRequestSubResult}, components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult, MsRequestSubResult},
props: { props: {
request: Object, request: Object,
scenarioName: String, scenarioName: String,

View File

@ -3,7 +3,7 @@
<el-card class="table-card" v-loading="loading"> <el-card class="table-card" v-loading="loading">
<template v-slot:header> <template v-slot:header>
<ms-table-header :condition.sync="condition" @search="selectByParam" title="" <ms-table-header :condition.sync="condition" @search="selectByParam" title=""
:show-create="false"/> :show-create="false" :tip="$t('commons.search_by_id_name_tag')"/>
</template> </template>
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table ms-select-all-fixed" <el-table ref="scenarioTable" border :data="tableData" class="adjust-table ms-select-all-fixed"

View File

@ -848,7 +848,7 @@
if (this.currentScenario.tags instanceof String) { if (this.currentScenario.tags instanceof String) {
this.currentScenario.tags = JSON.parse(this.currentScenario.tags); this.currentScenario.tags = JSON.parse(this.currentScenario.tags);
} }
this.$emit('refresh'); this.$emit('refresh',this.currentScenario);
}) })
} }
}) })

View File

@ -8,12 +8,22 @@
<el-form :model="form" :rules="rules" ref="from"> <el-form :model="form" :rules="rules" ref="from">
<el-form-item <el-form-item
prop="cronValue"> prop="cronValue">
<el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp" <el-row>
:placeholder="$t('schedule.please_input_cron_expression')"/> <el-col :span="18">
<el-button :disabled="isReadOnly" type="primary" @click="saveCron" v-tester>{{ <el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp"
$t('commons.save') :placeholder="$t('schedule.please_input_cron_expression')"/>
}} <el-button :disabled="isReadOnly" type="primary" @click="saveCron" v-tester>{{
</el-button> $t('commons.save')
}}
</el-button>
</el-col>
<el-col :span="6">
<schedule-switch :schedule="schedule" @scheduleChange="scheduleChange"></schedule-switch>
</el-col>
</el-row>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-link :disabled="isReadOnly" type="primary" @click="showCronDialog"> <el-link :disabled="isReadOnly" type="primary" @click="showCronDialog">
@ -44,6 +54,7 @@ import Crontab from "@/business/components/common/cron/Crontab";
import CrontabResult from "@/business/components/common/cron/CrontabResult"; import CrontabResult from "@/business/components/common/cron/CrontabResult";
import {cronValidate} from "@/common/js/cron"; import {cronValidate} from "@/common/js/cron";
import MsScheduleNotification from "./ScheduleNotification"; import MsScheduleNotification from "./ScheduleNotification";
import ScheduleSwitch from "@/business/components/api/automation/schedule/ScheduleSwitch";
function defaultCustomValidate() { function defaultCustomValidate() {
return {pass: true}; return {pass: true};
@ -55,7 +66,7 @@ const noticeTemplate = requireComponent.keys().length > 0 ? requireComponent("./
export default { export default {
name: "MsScheduleMaintain", name: "MsScheduleMaintain",
components: {CrontabResult, Crontab, MsScheduleNotification, "NoticeTemplate": noticeTemplate.default}, components: {CrontabResult, ScheduleSwitch,Crontab, MsScheduleNotification, "NoticeTemplate": noticeTemplate.default},
props: { props: {
customValidate: { customValidate: {
@ -100,6 +111,7 @@ export default {
form: { form: {
cronValue: "" cronValue: ""
}, },
paramRow:{},
activeName: 'first', activeName: 'first',
rules: { rules: {
cronValue: [{required: true, validator: validateCron, trigger: 'blur'}], cronValue: [{required: true, validator: validateCron, trigger: 'blur'}],
@ -110,6 +122,35 @@ export default {
currentUser: () => { currentUser: () => {
return getCurrentUser(); return getCurrentUser();
}, },
scheduleChange(){
let flag = this.schedule.enable;
this.$confirm(this.$t('api_test.home_page.running_task_list.confirm.close_title'), this.$t('commons.prompt'), {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
let param = {};
param.taskID = this.schedule.id;
param.enable = flag;
this.updateTask(param);
}).catch(() => {
});
},
updateTask(param){
this.result = this.$post('/api/schedule/updateEnableByPrimyKey', param, response => {
let paramTestId = "";
if (this.paramRow.redirectFrom == 'testPlan') {
paramTestId = this.paramRow.id;
this.scheduleTaskType = "TEST_PLAN_TEST";
} else {
paramTestId = this.paramRow.id;
this.scheduleTaskType = "API_SCENARIO_TEST";
}
this.taskID = paramTestId;
this.findSchedule(paramTestId);
});
},
initUserList() { initUserList() {
let param = { let param = {
name: '', name: '',
@ -132,6 +173,7 @@ export default {
open(row) { open(row) {
// //
let paramTestId = ""; let paramTestId = "";
this.paramRow = row;
if (row.redirectFrom == 'testPlan') { if (row.redirectFrom == 'testPlan') {
paramTestId = row.id; paramTestId = row.id;
this.scheduleTaskType = "TEST_PLAN_TEST"; this.scheduleTaskType = "TEST_PLAN_TEST";

View File

@ -0,0 +1,82 @@
<template>
<div class="schedule-config">
<div>
<span class="cron-ico">
<i class="el-icon-date" size="small"></i>
<span class="character">SCHEDULER</span>
</span>
<!-- <el-switch :disabled="!schedule.value || isReadOnly" v-model="schedule.enable" @change="scheduleChange"/>-->
<!-- <el-switch :disabled="!schedule.value || isReadOnly" v-model="schedule.enable" />-->
<el-switch :disabled="!schedule.value" v-model="schedule.enable" @change="scheduleChange"/>
</div>
<div>
<span>
{{ $t('schedule.next_execution_time') }}
<span :class="{'disable-character': !schedule.enable}"
v-if="!schedule.enable">{{ $t('schedule.not_set') }}</span>
<crontab-result v-if="schedule.enable" :enable-simple-mode="true" :ex="schedule.value" ref="crontabResult"/>
</span>
</div>
</div>
</template>
<script>
import CrontabResult from "@/business/components/common/cron/CrontabResult";
export default {
name: "ScheduleSwitch",
components: {CrontabResult},
data() {
return {
}
},
props: {
testId: String,
schedule: Object,
isReadOnly: {
type: Boolean,
default: false
}
},
methods: {
scheduleChange() {
this.$emit('scheduleChange');
},
},
watch: {
}
}
</script>
<style scoped>
.schedule-config {
float: right;
width: 250px;
height: 15px;
line-height: 25px;
}
.el-icon-date {
font-size: 20px;
margin-left: 5px;
}
.character {
font-weight: bold;
margin: 0 5px;
}
.disable-character {
color: #cccccc;
}
.el-switch {
margin: 0 5px;
}
.cron-ico {
cursor: pointer;
}
</style>

View File

@ -23,7 +23,7 @@
</div> </div>
</el-row> </el-row>
<el-dialog :title="$t('api_test.request.assertions.script')" :visible.sync="visible" width="900px"> <el-dialog :title="$t('api_test.request.assertions.script')" :visible.sync="visible" width="900px" append-to-body>
<el-row type="flex" justify="space-between" align="middle" class="quick-script-block"> <el-row type="flex" justify="space-between" align="middle" class="quick-script-block">
<div class="assertion-item input"> <div class="assertion-item input">
<el-input size="small" v-model="assertion.variable" <el-input size="small" v-model="assertion.variable"
@ -55,206 +55,206 @@
</template> </template>
<script> <script>
import {AssertionJSR223} from "../../model/ApiTestModel"; import {AssertionJSR223} from "../../model/ApiTestModel";
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter"; import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor"; import MsJsr233Processor from "../../../automation/scenario/common/Jsr233ProcessorContent";
export default { export default {
name: "MsApiAssertionJsr223", name: "MsApiAssertionJsr223",
components: {MsJsr233Processor, MsDialogFooter}, components: {MsJsr233Processor, MsDialogFooter},
props: { props: {
assertion: { assertion: {
default: () => { default: () => {
return new AssertionJSR223(); return new AssertionJSR223();
}
},
edit: {
type: Boolean,
default: false
},
index: Number,
list: Array,
callback: Function,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {
visible: false,
operators: {
EQ: {
label: "commons.adv_search.operators.equals",
value: "=="
},
NE: {
label: "commons.adv_search.operators.not_equals",
value: "!="
},
CONTAINS: {
label: "commons.adv_search.operators.like",
value: "contains"
},
NOT_CONTAINS: {
label: "commons.adv_search.operators.not_like",
value: "not contains"
},
GT: {
label: "commons.adv_search.operators.gt",
value: ">"
},
LT: {
label: "commons.adv_search.operators.lt",
value: "<"
},
IS_EMPTY: {
label: "commons.adv_search.operators.is_empty",
value: "is empty"
},
IS_NOT_EMPTY: {
label: "commons.adv_search.operators.is_not_empty",
value: "is not empty"
} }
}, },
templates: [ edit: {
{ type: Boolean,
title: this.$t('api_test.request.assertions.set_failure_status'), default: false
value: 'AssertionResult.setFailure(true)', },
index: Number,
list: Array,
callback: Function,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {
visible: false,
operators: {
EQ: {
label: "commons.adv_search.operators.equals",
value: "=="
},
NE: {
label: "commons.adv_search.operators.not_equals",
value: "!="
},
CONTAINS: {
label: "commons.adv_search.operators.like",
value: "contains"
},
NOT_CONTAINS: {
label: "commons.adv_search.operators.not_like",
value: "not contains"
},
GT: {
label: "commons.adv_search.operators.gt",
value: ">"
},
LT: {
label: "commons.adv_search.operators.lt",
value: "<"
},
IS_EMPTY: {
label: "commons.adv_search.operators.is_empty",
value: "is empty"
},
IS_NOT_EMPTY: {
label: "commons.adv_search.operators.is_not_empty",
value: "is not empty"
}
}, },
{ templates: [
title: this.$t('api_test.request.assertions.set_failure_msg'), {
value: 'AssertionResult.setFailureMessage("msg")', title: this.$t('api_test.request.assertions.set_failure_status'),
value: 'AssertionResult.setFailure(true)',
},
{
title: this.$t('api_test.request.assertions.set_failure_msg'),
value: 'AssertionResult.setFailureMessage("msg")',
}
],
}
},
methods: {
add() {
this.list.push(new AssertionJSR223(this.assertion));
this.callback();
},
remove() {
this.list.splice(this.index, 1);
},
changeOperator(value) {
if (value.indexOf("empty") > 0 && !!this.assertion.value) {
this.assertion.value = "";
} }
], this.quickScript();
} },
}, quickScript() {
if (this.assertion.variable && this.assertion.operator) {
let variable = this.assertion.variable;
let operator = this.assertion.operator;
let value = this.assertion.value || "";
let desc = "${" + variable + "} " + operator + " '" + value + "'";
let script = "value = vars.get(\"" + variable + "\");\n"
switch (this.assertion.operator) {
case "==":
script += "result = \"" + value + "\".equals(value);\n";
break;
case "!=":
script += "result = !\"" + value + "\".equals(value);\n";
break;
case "contains":
script += "result = value.contains(\"" + value + "\");\n";
break;
case "not contains":
script += "result = !value.contains(\"" + value + "\");\n";
break;
case ">":
desc = "${" + variable + "} " + operator + " " + value;
script += "number = Integer.parseInt(value);\n" +
"result = number > " + value + ";\n";
break;
case "<":
desc = "${" + variable + "} " + operator + " " + value;
script += "number = Integer.parseInt(value);\n" +
"result = number < " + value + ";\n";
break;
case "is empty":
desc = "${" + variable + "} " + operator
script += "result = value == void || value.length() == 0;\n";
break;
case "is not empty":
desc = "${" + variable + "} " + operator
script += "result = value != void && value.length() > 0;\n";
break;
}
let msg = "assertion [" + desc + "]: false;"
script += "if (!result){\n" +
"\tmsg = \"" + msg + "\";\n" +
"\tAssertionResult.setFailureMessage(msg);\n" +
"\tAssertionResult.setFailure(true);\n" +
"}";
methods: { this.assertion.desc = desc;
add() { this.assertion.script = script;
this.list.push(new AssertionJSR223(this.assertion)); this.$refs.jsr233.reload();
this.callback();
},
remove() {
this.list.splice(this.index, 1);
},
changeOperator(value) {
if (value.indexOf("empty") > 0 && !!this.assertion.value) {
this.assertion.value = "";
}
this.quickScript();
},
quickScript() {
if (this.assertion.variable && this.assertion.operator) {
let variable = this.assertion.variable;
let operator = this.assertion.operator;
let value = this.assertion.value || "";
let desc = "${" + variable + "} " + operator + " '" + value + "'";
let script = "value = vars.get(\"" + variable + "\");\n"
switch (this.assertion.operator) {
case "==":
script += "result = \"" + value + "\".equals(value);\n";
break;
case "!=":
script += "result = !\"" + value + "\".equals(value);\n";
break;
case "contains":
script += "result = value.contains(\"" + value + "\");\n";
break;
case "not contains":
script += "result = !value.contains(\"" + value + "\");\n";
break;
case ">":
desc = "${" + variable + "} " + operator + " " + value;
script += "number = Integer.parseInt(value);\n" +
"result = number > " + value + ";\n";
break;
case "<":
desc = "${" + variable + "} " + operator + " " + value;
script += "number = Integer.parseInt(value);\n" +
"result = number < " + value + ";\n";
break;
case "is empty":
desc = "${" + variable + "} " + operator
script += "result = value == void || value.length() == 0;\n";
break;
case "is not empty":
desc = "${" + variable + "} " + operator
script += "result = value != void && value.length() > 0;\n";
break;
} }
let msg = "assertion [" + desc + "]: false;"
script += "if (!result){\n" +
"\tmsg = \"" + msg + "\";\n" +
"\tAssertionResult.setFailureMessage(msg);\n" +
"\tAssertionResult.setFailure(true);\n" +
"}";
this.assertion.desc = desc; },
this.assertion.script = script; detail() {
this.$refs.jsr233.reload(); this.visible = true;
},
close() {
this.visible = false;
},
confirm() {
if (!this.edit) {
this.add();
}
if (!this.assertion.desc) {
this.assertion.desc = this.assertion.script;
}
this.close();
} }
},
}, computed: {
detail() { hasEmptyOperator() {
this.visible = true; return !!this.assertion.operator && this.assertion.operator.indexOf("empty") > 0;
},
close() {
this.visible = false;
},
confirm() {
if (!this.edit) {
this.add();
} }
if (!this.assertion.desc) {
this.assertion.desc = this.assertion.script;
}
this.close();
}
},
computed: {
hasEmptyOperator() {
return !!this.assertion.operator && this.assertion.operator.indexOf("empty") > 0;
} }
} }
}
</script> </script>
<style scoped> <style scoped>
.assertion-item { .assertion-item {
display: inline-block; display: inline-block;
} }
.assertion-item + .assertion-item { .assertion-item + .assertion-item {
margin-left: 10px; margin-left: 10px;
} }
.assertion-item.input { .assertion-item.input {
width: 100%; width: 100%;
} }
.assertion-item.select { .assertion-item.select {
min-width: 150px; min-width: 150px;
} }
.assertion-item.label { .assertion-item.label {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.assertion-item.btn { .assertion-item.btn {
min-width: 130px; min-width: 130px;
} }
.assertion-item.btn.circle { .assertion-item.btn.circle {
text-align: right; text-align: right;
min-width: 80px; min-width: 80px;
} }
.quick-script-block { .quick-script-block {
margin-bottom: 10px; margin-bottom: 10px;
} }
</style> </style>

View File

@ -3,13 +3,13 @@
<el-dialog <el-dialog
:title="$t('api_test.environment.select_environment')" :title="$t('api_test.environment.select_environment')"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
width="25%" width="15%"
:destroy-on-close="true" :destroy-on-close="true"
@close="handleClose" @close="handleClose"
> >
<el-form label-position="right" label-width="150px" size="medium" ref="form"> <el-form ref="form">
<el-form-item prop="type"> <el-form-item prop="type">
<el-select v-model="environmentId" value-key="id" size="small" class="ms-htt-width" <el-select v-model="environmentId" value-key="id" class="ms-htt-width"
:placeholder="$t('api_test.definition.request.run_env')" :placeholder="$t('api_test.definition.request.run_env')"
clearable> clearable>
<el-option v-for="(environment, index) in environments" :key="index" <el-option v-for="(environment, index) in environments" :key="index"
@ -22,6 +22,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template v-slot:footer> <template v-slot:footer>
<!-- <el-button onclick="this.handleClose">{{ $t('commons.cancel') }}</el-button>--> <!-- <el-button onclick="this.handleClose">{{ $t('commons.cancel') }}</el-button>-->
<el-button type="primary" @click="createPerformance" @keydown.enter.native.prevent> <el-button type="primary" @click="createPerformance" @keydown.enter.native.prevent>

View File

@ -213,7 +213,7 @@
}, },
copyCase(data) { copyCase(data) {
let uuid = getUUID(); let uuid = getUUID();
let obj = {name: "copy_" + data.name, priority: data.priority, active: true, request: data.request, uuid: uuid}; let obj = {name: "copy_" + data.name, priority: data.priority, active: true, tags: data.tags, request: data.request, uuid: uuid};
this.$emit('copyCase', obj); this.$emit('copyCase', obj);
}, },
selectTestCase(item, $event) { selectTestCase(item, $event) {

View File

@ -5,7 +5,7 @@
@isApiListEnableChange="isApiListEnableChange"> @isApiListEnableChange="isApiListEnableChange">
<el-link type="primary" style="float:right;margin-top: 5px" @click="open">{{$t('commons.adv_search.title')}}</el-link> <el-link type="primary" style="float:right;margin-top: 5px" @click="open">{{$t('commons.adv_search.title')}}</el-link>
<el-input placeholder="搜索" @blur="search" @keyup.enter.native="search" class="search-input" size="small" <el-input :placeholder="$t('commons.search_by_id_name_tag')" @blur="search" @keyup.enter.native="search" class="search-input" size="small"
v-model="condition.name"/> v-model="condition.name"/>
<el-table v-loading="result.loading" <el-table v-loading="result.loading"

View File

@ -5,7 +5,7 @@
@isApiListEnableChange="isApiListEnableChange"> @isApiListEnableChange="isApiListEnableChange">
<el-link type="primary" @click="open" style="float: right;margin-top: 5px">{{$t('commons.adv_search.title')}}</el-link> <el-link type="primary" @click="open" style="float: right;margin-top: 5px">{{$t('commons.adv_search.title')}}</el-link>
<el-input :placeholder="$t('api_monitor.please_search')" @blur="search" class="search-input" size="small" @keyup.enter.native="search" <el-input :placeholder="$t('commons.search_by_id_name_tag')" @blur="search" class="search-input" size="small" @keyup.enter.native="search"
v-model="condition.name"/> v-model="condition.name"/>
<el-table v-loading="result.loading" <el-table v-loading="result.loading"
@ -274,7 +274,12 @@
}, },
}, },
created: function () { created: function () {
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]}; if (this.trashEnable) {
this.condition.filters = {status: ["Trash"]};
}
else {
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
}
this.initTable(); this.initTable();
this.getMaintainerOptions(); this.getMaintainerOptions();
}, },

View File

@ -180,10 +180,10 @@
} }
}, },
//---使 //---使
createRootModel(){ createRootModel() {
let dataArr = this.$refs.nodeTree.extendTreeNodes; let dataArr = this.$refs.nodeTree.extendTreeNodes;
if(dataArr.length>0){ if (dataArr.length > 0) {
this.$refs.nodeTree.append({},dataArr[0]); this.$refs.nodeTree.append({}, dataArr[0]);
} }
}, },
exportAPI() { exportAPI() {
@ -197,7 +197,6 @@
}, },
refresh() { refresh() {
this.list(); this.list();
this.$emit("refreshTable");
}, },
} }
} }

View File

@ -35,8 +35,8 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-button class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-circle-plus-outline" @click="add">添加 <el-button class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-circle-plus-outline" @click="add">{{$t("commons.add")}}</el-button>
</el-button> <el-button class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-files" @click="copy">{{$t("commons.copy")}}</el-button>
</div> </div>
</template> </template>
@ -68,6 +68,16 @@
this.$emit('change', this.hostTable); this.$emit('change', this.hostTable);
}, },
add: function (r) { add: function (r) {
let row = {
ip: '',
domain: '',
status: 'edit',
annotation: '',
uuid: this.uuid(),
}
this.hostTable.push(row);
},
copy: function (r) {
let row = { let row = {
ip: '', ip: '',
domain: '', domain: '',

View File

@ -4,7 +4,7 @@
<span v-if="triggerMode === 'SCHEDULE'">{{$t('commons.trigger_mode.schedule')}}</span> <span v-if="triggerMode === 'SCHEDULE'">{{$t('commons.trigger_mode.schedule')}}</span>
<span v-if="triggerMode === 'TEST_PLAN_SCHEDULE'">{{$t('commons.trigger_mode.schedule')}}</span> <span v-if="triggerMode === 'TEST_PLAN_SCHEDULE'">{{$t('commons.trigger_mode.schedule')}}</span>
<span v-if="triggerMode === 'API'">{{$t('commons.trigger_mode.api')}}</span> <span v-if="triggerMode === 'API'">{{$t('commons.trigger_mode.api')}}</span>
<span v-if="triggerMode === 'CASE'">用例触发</span> <span v-if="triggerMode === 'CASE'">{{$t('commons.trigger_mode.case')}}</span>
</span> </span>
</template> </template>

View File

@ -143,6 +143,7 @@ export default {
{text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'}, {text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'},
{text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'}, {text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'},
{text: this.$t('commons.trigger_mode.api'), value: 'API'}, {text: this.$t('commons.trigger_mode.api'), value: 'API'},
{text: this.$t('commons.trigger_mode.case'), value: 'CASE'},
], ],
buttons: [ buttons: [
{ {

View File

@ -14,7 +14,7 @@
<el-tab-pane v-if="hasLicense()" :label="$t('display.title')" name="display"> <el-tab-pane v-if="hasLicense()" :label="$t('display.title')" name="display">
<ms-display/> <ms-display/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane v-if="hasLicense()" :label="'认证设置'" name="auth"> <el-tab-pane v-if="hasLicense()" :label="$t('auth_source.title')" name="auth">
<ms-auth/> <ms-auth/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>

View File

@ -15,6 +15,7 @@
:filter-node-method="filterNode" :filter-node-method="filterNode"
:expand-on-click-node="false" :expand-on-click-node="false"
highlight-current highlight-current
style="overflow: auto"
@node-click="nodeClick" @node-click="nodeClick"
ref="tree" ref="tree"
> >

View File

@ -31,14 +31,14 @@
@select="handleSelect" @select="handleSelect"
@cell-mouse-enter="showPopover" @cell-mouse-enter="showPopover"
row-key="id" row-key="id"
class="test-content adjust-table ms-select-all" class="test-content adjust-table ms-select-all-fixed"
ref="table" @row-click="handleEdit"> ref="table" @row-click="handleEdit">
<el-table-column <el-table-column
width="50" width="50"
type="selection"/> type="selection"/>
<ms-table-select-all <ms-table-header-select-popover v-show="total>0"
:page-size="pageSize > total ? total : pageSize" :page-size="pageSize > total ? total : pageSize"
:total="total" :total="total"
@selectPageAll="isSelectDataAll(false)" @selectPageAll="isSelectDataAll(false)"
@ -78,6 +78,7 @@
prop="priority" prop="priority"
:filters="priorityFilters" :filters="priorityFilters"
column-key="priority" column-key="priority"
min-width="100px"
:label="$t('test_track.case.priority')" :label="$t('test_track.case.priority')"
show-overflow-tooltip> show-overflow-tooltip>
<template v-slot:default="scope"> <template v-slot:default="scope">
@ -98,6 +99,7 @@
prop="method" prop="method"
column-key="method" column-key="method"
:filters="methodFilters" :filters="methodFilters"
min-width="100px"
:label="$t('test_track.case.method')" :label="$t('test_track.case.method')"
show-overflow-tooltip> show-overflow-tooltip>
<template v-slot:default="scope"> <template v-slot:default="scope">
@ -108,6 +110,7 @@
<el-table-column <el-table-column
:filters="statusFilters" :filters="statusFilters"
column-key="status" column-key="status"
min-width="100px"
:label="$t('test_track.case.status')"> :label="$t('test_track.case.status')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<span class="el-dropdown-link"> <span class="el-dropdown-link">
@ -127,6 +130,7 @@
<el-table-column <el-table-column
prop="nodePath" prop="nodePath"
:label="$t('test_track.case.module')" :label="$t('test_track.case.module')"
min-width="150px"
show-overflow-tooltip> show-overflow-tooltip>
</el-table-column> </el-table-column>
@ -134,12 +138,13 @@
prop="updateTime" prop="updateTime"
sortable="custom" sortable="custom"
:label="$t('commons.update_time')" :label="$t('commons.update_time')"
min-width="150px"
show-overflow-tooltip> show-overflow-tooltip>
<template v-slot:default="scope"> <template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span> <span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column fixed="right"
:label="$t('commons.operating')" min-width="150"> :label="$t('commons.operating')" min-width="150">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)" <ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)"
@ -169,6 +174,7 @@
<script> <script>
import MsCreateBox from '../../../settings/CreateBox'; import MsCreateBox from '../../../settings/CreateBox';
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
import TestCaseImport from '../components/TestCaseImport'; import TestCaseImport from '../components/TestCaseImport';
import TestCaseExport from '../components/TestCaseExport'; import TestCaseExport from '../components/TestCaseExport';
import MsTablePagination from '../../../../components/common/pagination/TablePagination'; import MsTablePagination from '../../../../components/common/pagination/TablePagination';
@ -191,7 +197,6 @@ import TestCaseDetail from "./TestCaseDetail";
import ReviewStatus from "@/business/components/track/case/components/ReviewStatus"; import ReviewStatus from "@/business/components/track/case/components/ReviewStatus";
import {getCurrentProjectID} from "../../../../../common/js/utils"; import {getCurrentProjectID} from "../../../../../common/js/utils";
import MsTag from "@/business/components/common/components/MsTag"; import MsTag from "@/business/components/common/components/MsTag";
import MsTableSelectAll from "../../../common/components/table/MsTableSelectAll";
import {_handleSelect, _handleSelectAll} from "../../../../../common/js/tableUtils"; import {_handleSelect, _handleSelectAll} from "../../../../../common/js/tableUtils";
import BatchMove from "./BatchMove"; import BatchMove from "./BatchMove";
@ -199,7 +204,7 @@ export default {
name: "TestCaseList", name: "TestCaseList",
components: { components: {
BatchMove, BatchMove,
MsTableSelectAll, MsTableHeaderSelectPopover,
MsTableButton, MsTableButton,
MsTableOperatorButton, MsTableOperatorButton,
MsTableOperator, MsTableOperator,

View File

@ -19,8 +19,8 @@
</el-table-column> </el-table-column>
<el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" width="200px"> <el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" width="200px">
<template v-slot:default="scope"> <template v-slot:default="scope">
<div v-for="itemName in scope.row.tagNames" :key="itemName"> <div v-for="itemName in getTagString(scope.row.tags)" :key="itemName">
<ms-tag type="success" effect="plain" :content="itemName"/> <ms-tag type="success" effect="plain" :content="itemName.substring(1, itemName.length-1)"/>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -101,6 +101,15 @@
}, },
}, },
methods: { methods: {
getTagString(tagsString) {
// if(tagsString.length == 2) {
if(tagsString.length >= 2 && tagsString[0] == '[' && tagsString[1] == ']') {
return null;
}
tagsString = tagsString.substring(1, tagsString.length - 1);
let tagList = tagsString.split(',');
return tagList;
},
search() { search() {
this.selectRows = new Set(); this.selectRows = new Set();
this.loading = true; this.loading = true;

View File

@ -76,6 +76,14 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="tags" :label="$t('commons.tag')">
<template v-slot:default="scope">
<div v-for="(tag, index) in scope.row.showTags" :key="tag + '_' + index">
<ms-tag type="success" effect="plain" :content="tag"/>
</div>
</template>
</el-table-column>
<el-table-column <el-table-column
prop="method" prop="method"
:filters="methodFilters" :filters="methodFilters"
@ -179,8 +187,9 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
min-width="100" fixed="right"
:label="$t('commons.operating')"> min-width="100"
:label="$t('commons.operating')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-table-operator-button :is-tester-permission="true" :tip="$t('commons.edit')" icon="el-icon-edit" <ms-table-operator-button :is-tester-permission="true" :tip="$t('commons.edit')" icon="el-icon-edit"
@exec="handleEdit(scope.row)"/> @exec="handleEdit(scope.row)"/>
@ -231,6 +240,7 @@ import ShowMoreBtn from "../../../../case/components/ShowMoreBtn";
import BatchEdit from "../../../../case/components/BatchEdit"; import BatchEdit from "../../../../case/components/BatchEdit";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic"; import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
import {hub} from "@/business/components/track/plan/event-bus"; import {hub} from "@/business/components/track/plan/event-bus";
import MsTag from "@/business/components/common/components/MsTag";
export default { export default {
name: "FunctionalTestCaseList", name: "FunctionalTestCaseList",
@ -243,7 +253,7 @@ export default {
StatusTableItem, StatusTableItem,
PriorityTableItem, StatusEdit, ExecutorEdit, MsTipButton, MsTablePagination, PriorityTableItem, StatusEdit, ExecutorEdit, MsTipButton, MsTablePagination,
MsTableHeader, NodeBreadcrumb, MsTableButton, ShowMoreBtn, MsTableHeader, NodeBreadcrumb, MsTableButton, ShowMoreBtn,
BatchEdit BatchEdit, MsTag
}, },
data() { data() {
return { return {
@ -257,7 +267,7 @@ export default {
currentPage: 1, currentPage: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,
status:'default', status: 'default',
selectRows: new Set(), selectRows: new Set(),
testPlan: {}, testPlan: {},
isReadOnly: false, isReadOnly: false,
@ -319,7 +329,7 @@ export default {
planId: { planId: {
type: String type: String
}, },
clickType:String, clickType: String,
selectNodeIds: { selectNodeIds: {
type: Array type: Array
}, },
@ -354,10 +364,10 @@ export default {
// param.planId = this.planId; // param.planId = this.planId;
this.condition.planId = this.planId; this.condition.planId = this.planId;
} }
if(this.clickType){ if (this.clickType) {
if(this.status =='default'){ if (this.status == 'default') {
this.condition.status = this.clickType; this.condition.status = this.clickType;
}else{ } else {
this.condition.status = null; this.condition.status = null;
} }
this.status = 'all'; this.status = 'all';
@ -373,6 +383,7 @@ export default {
this.tableData = data.listObject; this.tableData = data.listObject;
for (let i = 0; i < this.tableData.length; i++) { for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i]) { if (this.tableData[i]) {
this.$set(this.tableData[i], "showTags", JSON.parse(this.tableData[i].tags));
this.$set(this.tableData[i], "issuesSize", 0); this.$set(this.tableData[i], "issuesSize", 0);
this.$get("/issues/get/" + this.tableData[i].caseId).then(response => { this.$get("/issues/get/" + this.tableData[i].caseId).then(response => {
let issues = response.data.data; let issues = response.data.data;
@ -381,7 +392,11 @@ export default {
this.$set(this.tableData[i], "issuesContent", issues); this.$set(this.tableData[i], "issuesContent", issues);
} }
}).catch(() => { }).catch(() => {
this.$set(this.tableData[i], "issuesContent", [{title: '获取缺陷失败',description: '获取缺陷失败',platform: '获取缺陷失败' }]); this.$set(this.tableData[i], "issuesContent", [{
title: '获取缺陷失败',
description: '获取缺陷失败',
platform: '获取缺陷失败'
}]);
}) })
} }
} }
@ -597,4 +612,7 @@ export default {
cursor: pointer; cursor: pointer;
} }
.el-tag {
margin-left: 10px;
}
</style> </style>

View File

@ -150,7 +150,8 @@ export default {
name: "Trigger Mode", name: "Trigger Mode",
manual: "Manual trigger", manual: "Manual trigger",
schedule: "Scheduled Task", schedule: "Scheduled Task",
api: "API call" api: "API call",
case: "Case"
}, },
adv_search: { adv_search: {
title: 'Advanced Search', title: 'Advanced Search',
@ -602,6 +603,7 @@ export default {
customize_req: "Customize req", customize_req: "Customize req",
reference_info: "Reference info", reference_info: "Reference info",
scenario_test: "Scenario test", scenario_test: "Scenario test",
scenario_list: "Scenario List",
add_scenario: "Add scenario", add_scenario: "Add scenario",
scenario_name: "Scenario name", scenario_name: "Scenario name",
case_level: "Case level", case_level: "Case level",
@ -1499,6 +1501,7 @@ export default {
format: "Output format", format: "Output format",
}, },
auth_source: { auth_source: {
delete_prompt: 'This operation will delete the authentication source, do you want to continue? ' delete_prompt: 'This operation will delete the authentication source, do you want to continue? ',
title: 'Auth Source'
} }
}; };

View File

@ -151,7 +151,8 @@ export default {
name: "触发方式", name: "触发方式",
manual: "手动触发", manual: "手动触发",
schedule: "定时任务", schedule: "定时任务",
api: "API调用" api: "API调用",
case: "用例触发"
}, },
adv_search: { adv_search: {
title: '高级搜索', title: '高级搜索',
@ -603,6 +604,7 @@ export default {
customize_req: "自定义请求", customize_req: "自定义请求",
reference_info: "请选择接口或用例", reference_info: "请选择接口或用例",
scenario_test: "场景", scenario_test: "场景",
scenario_list: "场景列表",
add_scenario: "创建场景", add_scenario: "创建场景",
scenario_name: "场景名称", scenario_name: "场景名称",
case_level: "用例等级", case_level: "用例等级",
@ -1502,6 +1504,7 @@ export default {
format: "输出格式", format: "输出格式",
}, },
auth_source: { auth_source: {
delete_prompt: '此操作会删除认证源,是否继续?' delete_prompt: '此操作会删除认证源,是否继续?',
title: '认证设置'
} }
}; };

View File

@ -151,7 +151,8 @@ export default {
name: "觸發方式", name: "觸發方式",
manual: "手動觸發", manual: "手動觸發",
schedule: "定時任務", schedule: "定時任務",
api: "API調用" api: "API調用",
case: "用例觸發"
}, },
adv_search: { adv_search: {
title: '高級搜索', title: '高級搜索',
@ -602,6 +603,7 @@ export default {
customize_req: "自定義請求", customize_req: "自定義請求",
reference_info: "請選擇接口或用例", reference_info: "請選擇接口或用例",
scenario_test: "場景", scenario_test: "場景",
scenario_list: "場景列表",
add_scenario: "創建場景", add_scenario: "創建場景",
scenario_name: "場景名稱", scenario_name: "場景名稱",
case_level: "用例等級", case_level: "用例等級",
@ -1500,6 +1502,7 @@ export default {
format: "輸出格式", format: "輸出格式",
}, },
auth_source: { auth_source: {
delete_prompt: '此操作會刪除認證源,是否繼續? ' delete_prompt: '此操作會刪除認證源,是否繼續? ',
title: '認證設置'
} }
}; };