Merge branch 'master' of https://github.com/metersphere/metersphere
This commit is contained in:
commit
0fb4fdb8ae
|
@ -158,7 +158,8 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
if (!path.startsWith("/")) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.apache.jmeter.testelement.TestElement;
|
|||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
@ -51,7 +50,7 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
@JSONField(ordinal = 28)
|
||||
private String dataSourceId;
|
||||
@JSONField(ordinal = 29)
|
||||
private String protocol="SQL";
|
||||
private String protocol = "SQL";
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
|
||||
|
@ -62,7 +61,8 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
this.getRefElement(this);
|
||||
}
|
||||
if (StringUtils.isNotEmpty(dataSourceId)) {
|
||||
initDataSource();
|
||||
this.dataSource = null;
|
||||
this.initDataSource();
|
||||
}
|
||||
if (this.dataSource == null) {
|
||||
MSException.throwException("数据源为空无法执行");
|
||||
|
@ -79,14 +79,16 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
|
||||
private void initDataSource() {
|
||||
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
|
||||
ApiTestEnvironmentWithBLOBs environment = environmentService.get(this.dataSourceId);
|
||||
ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentId);
|
||||
if (environment != null && environment.getConfig() != null) {
|
||||
EnvironmentConfig config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
if (CollectionUtils.isNotEmpty(config.getDatabaseConfigs())) {
|
||||
List<DatabaseConfig> databaseConfigs = config.getDatabaseConfigs().stream().filter((DatabaseConfig d) -> this.dataSourceId.equals(d.getId())).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(databaseConfigs)) {
|
||||
this.dataSource = databaseConfigs.get(0);
|
||||
EnvironmentConfig envConfig = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) {
|
||||
envConfig.getDatabaseConfigs().forEach(item -> {
|
||||
if (item.getId().equals(this.dataSourceId)) {
|
||||
this.dataSource = item;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,6 +297,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
requestResult.setHeaders(result.getRequestHeaders());
|
||||
requestResult.setRequestSize(result.getSentBytes());
|
||||
requestResult.setStartTime(result.getStartTime());
|
||||
requestResult.setEndTime(result.getEndTime());
|
||||
requestResult.setTotalAssertions(result.getAssertionResults().length);
|
||||
requestResult.setSuccess(result.isSuccessful());
|
||||
requestResult.setError(result.getErrorCount());
|
||||
|
|
|
@ -18,6 +18,8 @@ public class RequestResult {
|
|||
|
||||
private long startTime;
|
||||
|
||||
private long endTime;
|
||||
|
||||
private int error;
|
||||
|
||||
private boolean success;
|
||||
|
|
|
@ -323,7 +323,9 @@ public class ApiDefinitionService {
|
|||
apiDefinitionMapper.updateByPrimaryKeyWithBLOBs(apiDefinition);
|
||||
}
|
||||
} else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) {
|
||||
if (CollectionUtils.isEmpty(sameRequest)) {
|
||||
batchMapper.insert(apiDefinition);
|
||||
}
|
||||
} else {
|
||||
if (CollectionUtils.isEmpty(sameRequest)) {
|
||||
batchMapper.insert(apiDefinition);
|
||||
|
|
|
@ -163,6 +163,7 @@ public class ApiScenarioReportService {
|
|||
String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError()));
|
||||
testPlanApiScenario.setPassRate(passRate);
|
||||
testPlanApiScenario.setReportId(report.getId());
|
||||
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
|
||||
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
|
||||
}
|
||||
returnReport = report;
|
||||
|
@ -220,6 +221,7 @@ public class ApiScenarioReportService {
|
|||
apiScenarioReportDetailMapper.insert(detail);
|
||||
|
||||
testPlanApiScenario.setReportId(report.getId());
|
||||
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
|
||||
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
|
||||
|
||||
lastReport = report;
|
||||
|
@ -343,14 +345,42 @@ public class ApiScenarioReportService {
|
|||
ids = allIds.stream().filter(id -> !reportRequest.getUnSelectIds().contains(id)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
//为预防数量太多,调用删除方法时引起SQL过长的Bug,此处采取分批执行的方式。
|
||||
//每次处理的数据数量
|
||||
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;
|
||||
}
|
||||
|
||||
//处理最后剩余的数据
|
||||
if(!ids.isEmpty()){
|
||||
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) {
|
||||
return extApiScenarioReportMapper.countByProjectID(projectId);
|
||||
|
|
|
@ -219,7 +219,8 @@
|
|||
</if>
|
||||
<if test="request.name != null">
|
||||
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 test="request.protocol != null">
|
||||
AND api_definition.protocol = #{request.protocol}
|
||||
|
|
|
@ -143,7 +143,9 @@
|
|||
</if>
|
||||
|
||||
<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 test="request.workspaceId != null">
|
||||
AND project.workspace_id = #{request.workspaceId}
|
||||
|
|
|
@ -284,7 +284,9 @@
|
|||
</foreach>
|
||||
</if>
|
||||
<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 test="request.createTime > 0">
|
||||
and t1.create_time >= #{request.createTime}
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
|
||||
<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,
|
||||
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_plan_test_case.update_time, test_case_node.name as model, project.name as projectName,
|
||||
test_plan_test_case.plan_id as planId
|
||||
|
@ -139,7 +139,7 @@
|
|||
</if>
|
||||
<if test="request.name != null">
|
||||
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 test="request.id != null">
|
||||
and test_case.id = #{request.id}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 387ca56312b62ae5edb3d7f34afa08946d86d621
|
||||
Subproject commit 26d36f3f81e889f58eed7c6903252a129b301d98
|
|
@ -1,11 +1,11 @@
|
|||
create table swagger_url_project
|
||||
(
|
||||
id varchar(255) not null,
|
||||
project_id varchar(255) null,
|
||||
id varchar(30) not null,
|
||||
project_id varchar(30) null,
|
||||
swagger_url varchar(255) null,
|
||||
module_id varchar(255) null,
|
||||
module_id varchar(30) null,
|
||||
module_path varchar(255) null,
|
||||
mode_id varchar(255) null,
|
||||
mode_id varchar(30) null,
|
||||
primary key (id)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<ms-main-container>
|
||||
<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
|
||||
:module-tree="nodeTree"
|
||||
:module-options="moduleOptions"
|
||||
|
@ -97,6 +97,7 @@
|
|||
renderComponent: true,
|
||||
isHide: true,
|
||||
activeName: 'default',
|
||||
redirectFlag: 'none',
|
||||
currentModule: null,
|
||||
moduleOptions: [],
|
||||
tabs: [],
|
||||
|
@ -150,6 +151,15 @@
|
|||
},
|
||||
changeRedirectParam(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) {
|
||||
if (!getCurrentProjectID()) {
|
||||
|
@ -227,8 +237,18 @@
|
|||
this.$refs.apiScenarioList.search(data);
|
||||
},
|
||||
refresh(data) {
|
||||
this.setTabTitle(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) {
|
||||
this.addTab({name: 'edit', currentScenario: row});
|
||||
},
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
throw e;
|
||||
}
|
||||
this.getFails();
|
||||
this.computeTotalTime();
|
||||
this.loading = false;
|
||||
} else {
|
||||
setTimeout(this.getReport, 2000)
|
||||
|
@ -146,12 +147,30 @@
|
|||
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) {
|
||||
this.active();
|
||||
this.isRequestResult = false;
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<div class="request-result">
|
||||
<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">
|
||||
<div class="method">
|
||||
{{request.method}}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-col :span="8">
|
||||
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
|
||||
<div class="url">{{request.url}}</div>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="url"> {{$t('api_report.start_time')}}:{{request.startTime | timestampFormatDate(true) }}
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-collapse-transition>
|
||||
|
@ -43,7 +47,7 @@
|
|||
|
||||
export default {
|
||||
name: "MsRequestResultTail",
|
||||
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult,MsRequestSubResult},
|
||||
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult, MsRequestSubResult},
|
||||
props: {
|
||||
request: Object,
|
||||
scenarioName: String,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<el-card class="table-card" v-loading="loading">
|
||||
<template v-slot:header>
|
||||
<ms-table-header :condition.sync="condition" @search="selectByParam" title=""
|
||||
:show-create="false"/>
|
||||
:show-create="false" :tip="$t('commons.search_by_id_name_tag')"/>
|
||||
</template>
|
||||
|
||||
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table ms-select-all-fixed"
|
||||
|
|
|
@ -848,7 +848,7 @@
|
|||
if (this.currentScenario.tags instanceof String) {
|
||||
this.currentScenario.tags = JSON.parse(this.currentScenario.tags);
|
||||
}
|
||||
this.$emit('refresh');
|
||||
this.$emit('refresh',this.currentScenario);
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,12 +8,22 @@
|
|||
<el-form :model="form" :rules="rules" ref="from">
|
||||
<el-form-item
|
||||
prop="cronValue">
|
||||
<el-row>
|
||||
<el-col :span="18">
|
||||
<el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp"
|
||||
:placeholder="$t('schedule.please_input_cron_expression')"/>
|
||||
<el-button :disabled="isReadOnly" type="primary" @click="saveCron" v-tester>{{
|
||||
$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-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 {cronValidate} from "@/common/js/cron";
|
||||
import MsScheduleNotification from "./ScheduleNotification";
|
||||
import ScheduleSwitch from "@/business/components/api/automation/schedule/ScheduleSwitch";
|
||||
|
||||
function defaultCustomValidate() {
|
||||
return {pass: true};
|
||||
|
@ -55,7 +66,7 @@ const noticeTemplate = requireComponent.keys().length > 0 ? requireComponent("./
|
|||
|
||||
export default {
|
||||
name: "MsScheduleMaintain",
|
||||
components: {CrontabResult, Crontab, MsScheduleNotification, "NoticeTemplate": noticeTemplate.default},
|
||||
components: {CrontabResult, ScheduleSwitch,Crontab, MsScheduleNotification, "NoticeTemplate": noticeTemplate.default},
|
||||
|
||||
props: {
|
||||
customValidate: {
|
||||
|
@ -100,6 +111,7 @@ export default {
|
|||
form: {
|
||||
cronValue: ""
|
||||
},
|
||||
paramRow:{},
|
||||
activeName: 'first',
|
||||
rules: {
|
||||
cronValue: [{required: true, validator: validateCron, trigger: 'blur'}],
|
||||
|
@ -110,6 +122,35 @@ export default {
|
|||
currentUser: () => {
|
||||
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() {
|
||||
let param = {
|
||||
name: '',
|
||||
|
@ -132,6 +173,7 @@ export default {
|
|||
open(row) {
|
||||
//测试计划页面跳转来的
|
||||
let paramTestId = "";
|
||||
this.paramRow = row;
|
||||
if (row.redirectFrom == 'testPlan') {
|
||||
paramTestId = row.id;
|
||||
this.scheduleTaskType = "TEST_PLAN_TEST";
|
||||
|
|
|
@ -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>
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
</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">
|
||||
<div class="assertion-item input">
|
||||
<el-input size="small" v-model="assertion.variable"
|
||||
|
@ -55,11 +55,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {AssertionJSR223} from "../../model/ApiTestModel";
|
||||
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import {AssertionJSR223} from "../../model/ApiTestModel";
|
||||
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
|
||||
import MsJsr233Processor from "../../../automation/scenario/common/Jsr233ProcessorContent";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: "MsApiAssertionJsr223",
|
||||
components: {MsJsr233Processor, MsDialogFooter},
|
||||
props: {
|
||||
|
@ -219,42 +219,42 @@ export default {
|
|||
return !!this.assertion.operator && this.assertion.operator.indexOf("empty") > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.assertion-item {
|
||||
.assertion-item {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.assertion-item + .assertion-item {
|
||||
.assertion-item + .assertion-item {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.assertion-item.input {
|
||||
.assertion-item.input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.assertion-item.select {
|
||||
.assertion-item.select {
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.assertion-item.label {
|
||||
.assertion-item.label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.assertion-item.btn {
|
||||
.assertion-item.btn {
|
||||
min-width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
.assertion-item.btn.circle {
|
||||
.assertion-item.btn.circle {
|
||||
text-align: right;
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-script-block {
|
||||
.quick-script-block {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
<el-dialog
|
||||
:title="$t('api_test.environment.select_environment')"
|
||||
:visible.sync="dialogVisible"
|
||||
width="25%"
|
||||
width="15%"
|
||||
:destroy-on-close="true"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form label-position="right" label-width="150px" size="medium" ref="form">
|
||||
<el-form ref="form">
|
||||
<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')"
|
||||
clearable>
|
||||
<el-option v-for="(environment, index) in environments" :key="index"
|
||||
|
@ -22,6 +22,7 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template v-slot:footer>
|
||||
<!-- <el-button onclick="this.handleClose">{{ $t('commons.cancel') }}</el-button>-->
|
||||
<el-button type="primary" @click="createPerformance" @keydown.enter.native.prevent>
|
||||
|
|
|
@ -213,7 +213,7 @@
|
|||
},
|
||||
copyCase(data) {
|
||||
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);
|
||||
},
|
||||
selectTestCase(item, $event) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
@isApiListEnableChange="isApiListEnableChange">
|
||||
<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"/>
|
||||
|
||||
<el-table v-loading="result.loading"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
@isApiListEnableChange="isApiListEnableChange">
|
||||
|
||||
<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"/>
|
||||
|
||||
<el-table v-loading="result.loading"
|
||||
|
@ -274,7 +274,12 @@
|
|||
},
|
||||
},
|
||||
created: function () {
|
||||
if (this.trashEnable) {
|
||||
this.condition.filters = {status: ["Trash"]};
|
||||
}
|
||||
else {
|
||||
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
|
||||
}
|
||||
this.initTable();
|
||||
this.getMaintainerOptions();
|
||||
},
|
||||
|
|
|
@ -180,10 +180,10 @@
|
|||
}
|
||||
},
|
||||
//创建根目录的模块---供父类使用
|
||||
createRootModel(){
|
||||
createRootModel() {
|
||||
let dataArr = this.$refs.nodeTree.extendTreeNodes;
|
||||
if(dataArr.length>0){
|
||||
this.$refs.nodeTree.append({},dataArr[0]);
|
||||
if (dataArr.length > 0) {
|
||||
this.$refs.nodeTree.append({}, dataArr[0]);
|
||||
}
|
||||
},
|
||||
exportAPI() {
|
||||
|
@ -197,7 +197,6 @@
|
|||
},
|
||||
refresh() {
|
||||
this.list();
|
||||
this.$emit("refreshTable");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-button class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-circle-plus-outline" @click="add">添加
|
||||
</el-button>
|
||||
<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 class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-files" @click="copy">{{$t("commons.copy")}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -68,6 +68,16 @@
|
|||
this.$emit('change', this.hostTable);
|
||||
},
|
||||
add: function (r) {
|
||||
let row = {
|
||||
ip: '',
|
||||
domain: '',
|
||||
status: 'edit',
|
||||
annotation: '',
|
||||
uuid: this.uuid(),
|
||||
}
|
||||
this.hostTable.push(row);
|
||||
},
|
||||
copy: function (r) {
|
||||
let row = {
|
||||
ip: '',
|
||||
domain: '',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<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 === '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>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ export default {
|
|||
{text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'},
|
||||
{text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'},
|
||||
{text: this.$t('commons.trigger_mode.api'), value: 'API'},
|
||||
{text: this.$t('commons.trigger_mode.case'), value: 'CASE'},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<el-tab-pane v-if="hasLicense()" :label="$t('display.title')" name="display">
|
||||
<ms-display/>
|
||||
</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/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
:filter-node-method="filterNode"
|
||||
:expand-on-click-node="false"
|
||||
highlight-current
|
||||
style="overflow: auto"
|
||||
@node-click="nodeClick"
|
||||
ref="tree"
|
||||
>
|
||||
|
|
|
@ -31,14 +31,14 @@
|
|||
@select="handleSelect"
|
||||
@cell-mouse-enter="showPopover"
|
||||
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">
|
||||
|
||||
<el-table-column
|
||||
width="50"
|
||||
type="selection"/>
|
||||
|
||||
<ms-table-select-all
|
||||
<ms-table-header-select-popover v-show="total>0"
|
||||
:page-size="pageSize > total ? total : pageSize"
|
||||
:total="total"
|
||||
@selectPageAll="isSelectDataAll(false)"
|
||||
|
@ -78,6 +78,7 @@
|
|||
prop="priority"
|
||||
:filters="priorityFilters"
|
||||
column-key="priority"
|
||||
min-width="100px"
|
||||
:label="$t('test_track.case.priority')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
|
@ -98,6 +99,7 @@
|
|||
prop="method"
|
||||
column-key="method"
|
||||
:filters="methodFilters"
|
||||
min-width="100px"
|
||||
:label="$t('test_track.case.method')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
|
@ -108,6 +110,7 @@
|
|||
<el-table-column
|
||||
:filters="statusFilters"
|
||||
column-key="status"
|
||||
min-width="100px"
|
||||
:label="$t('test_track.case.status')">
|
||||
<template v-slot:default="scope">
|
||||
<span class="el-dropdown-link">
|
||||
|
@ -127,6 +130,7 @@
|
|||
<el-table-column
|
||||
prop="nodePath"
|
||||
:label="$t('test_track.case.module')"
|
||||
min-width="150px"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
|
||||
|
@ -134,12 +138,13 @@
|
|||
prop="updateTime"
|
||||
sortable="custom"
|
||||
:label="$t('commons.update_time')"
|
||||
min-width="150px"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
<el-table-column fixed="right"
|
||||
:label="$t('commons.operating')" min-width="150">
|
||||
<template v-slot:default="scope">
|
||||
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)"
|
||||
|
@ -169,6 +174,7 @@
|
|||
<script>
|
||||
|
||||
import MsCreateBox from '../../../settings/CreateBox';
|
||||
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
|
||||
import TestCaseImport from '../components/TestCaseImport';
|
||||
import TestCaseExport from '../components/TestCaseExport';
|
||||
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 {getCurrentProjectID} from "../../../../../common/js/utils";
|
||||
import MsTag from "@/business/components/common/components/MsTag";
|
||||
import MsTableSelectAll from "../../../common/components/table/MsTableSelectAll";
|
||||
import {_handleSelect, _handleSelectAll} from "../../../../../common/js/tableUtils";
|
||||
import BatchMove from "./BatchMove";
|
||||
|
||||
|
@ -199,7 +204,7 @@ export default {
|
|||
name: "TestCaseList",
|
||||
components: {
|
||||
BatchMove,
|
||||
MsTableSelectAll,
|
||||
MsTableHeaderSelectPopover,
|
||||
MsTableButton,
|
||||
MsTableOperatorButton,
|
||||
MsTableOperator,
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" width="200px">
|
||||
<template v-slot:default="scope">
|
||||
<div v-for="itemName in scope.row.tagNames" :key="itemName">
|
||||
<ms-tag type="success" effect="plain" :content="itemName"/>
|
||||
<div v-for="itemName in getTagString(scope.row.tags)" :key="itemName">
|
||||
<ms-tag type="success" effect="plain" :content="itemName.substring(1, itemName.length-1)"/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -101,6 +101,15 @@
|
|||
},
|
||||
},
|
||||
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() {
|
||||
this.selectRows = new Set();
|
||||
this.loading = true;
|
||||
|
|
|
@ -76,6 +76,14 @@
|
|||
</template>
|
||||
</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
|
||||
prop="method"
|
||||
:filters="methodFilters"
|
||||
|
@ -179,6 +187,7 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
fixed="right"
|
||||
min-width="100"
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
|
@ -231,6 +240,7 @@ import ShowMoreBtn from "../../../../case/components/ShowMoreBtn";
|
|||
import BatchEdit from "../../../../case/components/BatchEdit";
|
||||
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
|
||||
import {hub} from "@/business/components/track/plan/event-bus";
|
||||
import MsTag from "@/business/components/common/components/MsTag";
|
||||
|
||||
export default {
|
||||
name: "FunctionalTestCaseList",
|
||||
|
@ -243,7 +253,7 @@ export default {
|
|||
StatusTableItem,
|
||||
PriorityTableItem, StatusEdit, ExecutorEdit, MsTipButton, MsTablePagination,
|
||||
MsTableHeader, NodeBreadcrumb, MsTableButton, ShowMoreBtn,
|
||||
BatchEdit
|
||||
BatchEdit, MsTag
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -257,7 +267,7 @@ export default {
|
|||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
status:'default',
|
||||
status: 'default',
|
||||
selectRows: new Set(),
|
||||
testPlan: {},
|
||||
isReadOnly: false,
|
||||
|
@ -319,7 +329,7 @@ export default {
|
|||
planId: {
|
||||
type: String
|
||||
},
|
||||
clickType:String,
|
||||
clickType: String,
|
||||
selectNodeIds: {
|
||||
type: Array
|
||||
},
|
||||
|
@ -354,10 +364,10 @@ export default {
|
|||
// param.planId = this.planId;
|
||||
this.condition.planId = this.planId;
|
||||
}
|
||||
if(this.clickType){
|
||||
if(this.status =='default'){
|
||||
if (this.clickType) {
|
||||
if (this.status == 'default') {
|
||||
this.condition.status = this.clickType;
|
||||
}else{
|
||||
} else {
|
||||
this.condition.status = null;
|
||||
}
|
||||
this.status = 'all';
|
||||
|
@ -373,6 +383,7 @@ export default {
|
|||
this.tableData = data.listObject;
|
||||
for (let i = 0; i < this.tableData.length; 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.$get("/issues/get/" + this.tableData[i].caseId).then(response => {
|
||||
let issues = response.data.data;
|
||||
|
@ -381,7 +392,11 @@ export default {
|
|||
this.$set(this.tableData[i], "issuesContent", issues);
|
||||
}
|
||||
}).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;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -150,7 +150,8 @@ export default {
|
|||
name: "Trigger Mode",
|
||||
manual: "Manual trigger",
|
||||
schedule: "Scheduled Task",
|
||||
api: "API call"
|
||||
api: "API call",
|
||||
case: "Case"
|
||||
},
|
||||
adv_search: {
|
||||
title: 'Advanced Search',
|
||||
|
@ -602,6 +603,7 @@ export default {
|
|||
customize_req: "Customize req",
|
||||
reference_info: "Reference info",
|
||||
scenario_test: "Scenario test",
|
||||
scenario_list: "Scenario List",
|
||||
add_scenario: "Add scenario",
|
||||
scenario_name: "Scenario name",
|
||||
case_level: "Case level",
|
||||
|
@ -1499,6 +1501,7 @@ export default {
|
|||
format: "Output format",
|
||||
},
|
||||
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'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -151,7 +151,8 @@ export default {
|
|||
name: "触发方式",
|
||||
manual: "手动触发",
|
||||
schedule: "定时任务",
|
||||
api: "API调用"
|
||||
api: "API调用",
|
||||
case: "用例触发"
|
||||
},
|
||||
adv_search: {
|
||||
title: '高级搜索',
|
||||
|
@ -603,6 +604,7 @@ export default {
|
|||
customize_req: "自定义请求",
|
||||
reference_info: "请选择接口或用例",
|
||||
scenario_test: "场景",
|
||||
scenario_list: "场景列表",
|
||||
add_scenario: "创建场景",
|
||||
scenario_name: "场景名称",
|
||||
case_level: "用例等级",
|
||||
|
@ -1502,6 +1504,7 @@ export default {
|
|||
format: "输出格式",
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt: '此操作会删除认证源,是否继续?'
|
||||
delete_prompt: '此操作会删除认证源,是否继续?',
|
||||
title: '认证设置'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -151,7 +151,8 @@ export default {
|
|||
name: "觸發方式",
|
||||
manual: "手動觸發",
|
||||
schedule: "定時任務",
|
||||
api: "API調用"
|
||||
api: "API調用",
|
||||
case: "用例觸發"
|
||||
},
|
||||
adv_search: {
|
||||
title: '高級搜索',
|
||||
|
@ -602,6 +603,7 @@ export default {
|
|||
customize_req: "自定義請求",
|
||||
reference_info: "請選擇接口或用例",
|
||||
scenario_test: "場景",
|
||||
scenario_list: "場景列表",
|
||||
add_scenario: "創建場景",
|
||||
scenario_name: "場景名稱",
|
||||
case_level: "用例等級",
|
||||
|
@ -1500,6 +1502,7 @@ export default {
|
|||
format: "輸出格式",
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt: '此操作會刪除認證源,是否繼續? '
|
||||
delete_prompt: '此操作會刪除認證源,是否繼續? ',
|
||||
title: '認證設置'
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue