Merge remote-tracking branch 'origin/master'

This commit is contained in:
wenyann 2020-08-20 16:51:06 +08:00
commit a722d6419e
51 changed files with 2454 additions and 1972 deletions

2
backend/.gitignore vendored
View File

@ -32,4 +32,4 @@ target
.project
.classpath
.factorypath
*.jar
src/main/resources/jmeter/lib/

View File

@ -15,4 +15,5 @@ public class Scenario {
private List<KeyValue> headers;
private List<Request> requests;
private DubboConfig dubboConfig;
private Boolean enable;
}

View File

@ -49,4 +49,6 @@ public class DubboRequest implements Request {
private BeanShellPreProcessor beanShellPreProcessor;
@JSONField(ordinal = 13)
private BeanShellPostProcessor beanShellPostProcessor;
@JSONField(ordinal = 14)
private Boolean enable;
}

View File

@ -41,4 +41,10 @@ public class HttpRequest implements Request {
private BeanShellPreProcessor beanShellPreProcessor;
@JSONField(ordinal = 12)
private BeanShellPostProcessor beanShellPostProcessor;
@JSONField(ordinal = 13)
private Boolean enable;
@JSONField(ordinal = 14)
private Integer connectTimeout;
@JSONField(ordinal = 15)
private Integer responseTimeout;
}

View File

@ -149,9 +149,12 @@ public class JmeterDocumentParser {
String path = ele.getTextContent();
Map<String, String> parser = parserUrl(path);
String url = parser.get("URL");
String params = parser.keySet().stream().filter(k -> !"URL".equals(k)).reduce("", (u, k) -> {
String params = parser.keySet().stream().filter(k -> !"URL".equals(k)).reduce("?", (u, k) -> {
String v = parser.get(k);
u += "&" + k + "=" + ScriptEngineUtils.calculate(v);
if (!StringUtils.equals("?", u)) {
u += "&";
}
u += k + "=" + ScriptEngineUtils.calculate(v);
return u;
});
ele.setTextContent(url + params);

View File

@ -18,15 +18,13 @@ import io.metersphere.commons.constants.FileType;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.constants.ScheduleType;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao;
import io.metersphere.i18n.Translator;
import io.metersphere.job.sechedule.ApiTestJob;
import io.metersphere.service.FileService;
import io.metersphere.service.QuotaService;
import io.metersphere.service.ScheduleService;
import io.metersphere.track.service.TestCaseService;
import org.apache.dubbo.common.URL;
@ -37,6 +35,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -92,6 +91,7 @@ public class APITestService {
}
public void copy(SaveAPITestRequest request) {
checkQuota();
request.setName(request.getName() + " Copy");
try {
checkNameExist(request);
@ -355,4 +355,11 @@ public class APITestService {
jMeterService.run(request.getId(), reportId, is);
return reportId;
}
private void checkQuota() {
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
if (quotaService != null) {
quotaService.checkAPIQuota();
}
}
}

View File

@ -115,7 +115,7 @@
</select>
<select id="list" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
select test_plan_test_case.*, test_case.*,test_case_node.name as model
select test_case.remark, test_plan_test_case.*,test_case.*,test_case_node.name as model
from test_plan_test_case
inner join test_case on test_plan_test_case.case_id = test_case.id left join test_case_node on
test_case_node.id=test_case.node_id

View File

@ -16,11 +16,19 @@ public class CommonBeanFactory implements ApplicationContextAware {
}
public static Object getBean(String beanName) {
try {
return context != null && !StringUtils.isBlank(beanName) ? context.getBean(beanName) : null;
} catch (BeansException e) {
return null;
}
}
public static <T> T getBean(Class<T> className) {
try {
return context != null && className != null ? context.getBean(className) : null;
} catch (BeansException e) {
return null;
}
}
}

View File

@ -0,0 +1,6 @@
package io.metersphere.service;
public interface QuotaService {
void checkAPIQuota();
}

View File

@ -235,7 +235,7 @@ public class IssuesService {
" \"key\":\"" + jiraKey + "\"\n" +
" },\n" +
" \"summary\":\"" + issuesRequest.getTitle() + "\",\n" +
" \"description\":\"" + issuesRequest.getContent() + "\",\n" +
" \"description\": " + JSON.toJSONString(issuesRequest.getContent()) + ",\n" +
" \"issuetype\":{\n" +
" \"id\":\"10009\",\n" +
" \"name\":\"Defect\"\n" +

View File

@ -0,0 +1,36 @@
package io.metersphere.track.service;
import io.metersphere.base.domain.TestCaseIssues;
import io.metersphere.base.domain.TestCaseIssuesExample;
import io.metersphere.base.mapper.IssuesMapper;
import io.metersphere.base.mapper.TestCaseIssuesMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestCaseIssueService {
@Resource
private TestCaseIssuesMapper testCaseIssuesMapper;
@Resource
private IssuesMapper issuesMapper;
public void delTestCaseIssues(String testCaseId) {
TestCaseIssuesExample example = new TestCaseIssuesExample();
example.createCriteria().andTestCaseIdEqualTo(testCaseId);
List<TestCaseIssues> testCaseIssues = testCaseIssuesMapper.selectByExample(example);
if (!CollectionUtils.isEmpty(testCaseIssues)) {
List<String> list = testCaseIssues.stream().map(TestCaseIssues::getIssuesId).collect(Collectors.toList());
list.forEach(id -> {
issuesMapper.deleteByPrimaryKey(id);
});
}
testCaseIssuesMapper.deleteByExample(example);
}
}

View File

@ -73,6 +73,9 @@ public class TestCaseService {
@Resource
UserRoleMapper userRoleMapper;
@Resource
TestCaseIssueService testCaseIssueService;
public void addTestCase(TestCaseWithBLOBs testCase) {
testCase.setName(testCase.getName());
checkTestCaseExist(testCase);
@ -149,6 +152,7 @@ public class TestCaseService {
TestPlanTestCaseExample example = new TestPlanTestCaseExample();
example.createCriteria().andCaseIdEqualTo(testCaseId);
testPlanTestCaseMapper.deleteByExample(example);
testCaseIssueService.delTestCaseIssues(testCaseId);
return testCaseMapper.deleteByPrimaryKey(testCaseId);
}
@ -426,6 +430,7 @@ public class TestCaseService {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, request);
testCase.setUpdateTime(System.currentTimeMillis());
testCaseMapper.updateByExampleSelective(
testCase,
testCaseExample);

@ -1 +1 @@
Subproject commit 8eff343619df1572e1cded52f173257ef4b518a1
Subproject commit b86032cbbda9a9e6028308aa95a887cff2192f1c

View File

@ -142,3 +142,5 @@ quota_workspace_excess_org_performance=The total number of performance tests for
quota_workspace_excess_org_max_threads=The maximum concurrent number of workspaces cannot exceed the quota of the organization
quota_workspace_excess_org_max_duration=The stress test duration of the workspace cannot exceed the organization's quota
quota_workspace_excess_org_resource_pool=The resource pool of the workspace cannot exceed the resource pool of the organization
quota_api_excess_workspace=The number of interface tests exceeds the workspace quota
quota_api_excess_organization=The number of interface tests exceeds the organization quota

View File

@ -142,6 +142,8 @@ quota_workspace_excess_org_performance=工作空间的性能测试数量总和
quota_workspace_excess_org_max_threads=工作空间的最大并发数不能超过组织的配额
quota_workspace_excess_org_max_duration=工作空间的压测时长不能超过组织的配额
quota_workspace_excess_org_resource_pool=工作空间的资源池不能超过组织的资源池范围
quota_api_excess_workspace=接口测试数量超过工作空间限额
quota_api_excess_organization=接口测试数量超过组织限额

View File

@ -142,3 +142,5 @@ quota_workspace_excess_org_performance=工作空間的性能測試數量總和
quota_workspace_excess_org_max_threads=工作空間的最大並發數不能超過組織的配額
quota_workspace_excess_org_max_duration=工作空間的壓測時長不能超過組織的配額
quota_workspace_excess_org_resource_pool=工作空間的資源池不能超過組織的資源池範圍
quota_api_excess_workspace=接口測試數量超過工作空間限額
quota_api_excess_organization=接口測試數量超過組織限額

View File

@ -9,7 +9,7 @@
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="3">
<template v-slot:title>{{ $t('commons.project') }}</template>
<ms-recent-list :options="projectRecent"/>
<ms-recent-list ref="projectRecent" :options="projectRecent"/>
<el-divider class="menu-divider"/>
<ms-show-all :index="'/api/project/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/api/project/create'"
@ -18,7 +18,7 @@
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="4">
<template v-slot:title>{{ $t('commons.test') }}</template>
<ms-recent-list :options="testRecent"/>
<ms-recent-list ref="testRecent" :options="testRecent"/>
<el-divider class="menu-divider"/>
<ms-show-all :index="'/api/test/list/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/api/test/create'"
@ -27,7 +27,7 @@
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="5">
<template v-slot:title>{{ $t('commons.report') }}</template>
<ms-recent-list :options="reportRecent"/>
<ms-recent-list ref="reportRecent" :options="reportRecent"/>
<el-divider class="menu-divider"/>
<ms-show-all :index="'/api/report/list/all'"/>
</el-submenu>
@ -50,6 +50,7 @@
import MsShowAll from "../../common/head/ShowAll";
import MsCreateButton from "../../common/head/CreateButton";
import MsCreateTest from "../../common/head/CreateTest";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
export default {
name: "MsApiHeaderMenus",
@ -85,6 +86,13 @@
}
}
}
},
mounted() {
ApiEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.testRecent.recent();
this.$refs.reportRecent.recent();
});
}
}

View File

@ -58,6 +58,7 @@
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
export default {
components: {
@ -127,6 +128,8 @@
this.result = this.$post("/api/report/delete", {id: report.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
});
}
}

View File

@ -68,9 +68,9 @@
</el-tabs>
<div v-else>
<ms-request-metric :request="request"/>
<ms-request-text :request="request"/>
<ms-request-text v-if="isCodeEditAlive" :request="request"/>
<br>
<ms-response-text :response="request.responseResult"/>
<ms-response-text v-if="isCodeEditAlive" :response="request.responseResult"/>
</div>
</div>
</el-collapse-transition>
@ -96,12 +96,17 @@
return {
isActive: true,
activeName: "sub",
isCodeEditAlive: true
}
},
methods: {
active() {
this.isActive = !this.isActive;
},
reload() {
this.isCodeEditAlive = false;
this.$nextTick(() => (this.isCodeEditAlive = true));
}
},
@ -111,7 +116,7 @@
},
hasSub() {
return this.request.subRequestResults.length > 0;
}
},
}
}
</script>

View File

@ -66,6 +66,7 @@
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "@/common/js/utils";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
export default {
components: {
@ -183,6 +184,8 @@
this.result = this.$post("/api/delete", {id: test.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
});
}
}

View File

@ -0,0 +1,31 @@
<template>
<div>
<el-form :model="request" ref="request" label-width="100px">
<el-form-item :label="$t('api_test.request.connect_timeout')" prop="connectTimeout">
<el-input-number size="small" :disabled="isReadOnly" v-model="request.connectTimeout" :placeholder="$t('commons.millisecond')" :maxlength="1000*10000000" />
</el-form-item>
<el-form-item :label="$t('api_test.request.response_timeout')" prop="responseTimeout">
<el-input-number size="small" :disabled="isReadOnly" v-model="request.responseTimeout" :placeholder="$t('commons.millisecond')" :maxlength="1000*10000000"/>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "MsApiAdvancedConfig",
props: {
request: Object,
isReadOnly: {
type: Boolean,
default: false
}
}
}
</script>
<style scoped>
</style>

View File

@ -5,7 +5,7 @@
<ms-api-collapse v-model="activeName" @change="handleChange" accordion>
<draggable :list="scenarios" group="Scenario" class="scenario-draggable" ghost-class="scenario-ghost">
<ms-api-collapse-item v-for="(scenario, index) in scenarios" :key="index"
:title="scenario.name" :name="index">
:title="scenario.name" :name="index" :class="{'disable-scenario': !scenario.enable}">
<template slot="title">
<div class="scenario-name">
{{scenario.name}}
@ -22,6 +22,12 @@
<el-dropdown-item :disabled="isReadOnly" :command="{type:'delete', index:index}">
{{$t('api_test.scenario.delete')}}
</el-dropdown-item>
<el-dropdown-item v-if="scenario.enable" :disabled="isReadOnly" :command="{type:'disable', index:index}">
{{$t('api_test.scenario.disable')}}
</el-dropdown-item>
<el-dropdown-item v-if="!scenario.enable" :disabled="isReadOnly" :command="{type:'enable', index:index}">
{{$t('api_test.scenario.enable')}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
@ -108,6 +114,12 @@ export default {
this.select(this.scenarios[0]);
}
},
disableScenario: function (index) {
this.scenarios[index].enable = false;
},
enableScenario: function (index) {
this.scenarios[index].enable = true;
},
handleChange: function (index) {
this.select(this.scenarios[index]);
},
@ -119,6 +131,12 @@ export default {
case "delete":
this.deleteScenario(command.index);
break;
case "disable":
this.disableScenario(command.index);
break;
case "enable":
this.enableScenario(command.index);
break;
}
},
select: function (obj, scenario) {
@ -203,9 +221,9 @@ export default {
width: 100%;
}
.scenario-name > #hint {
color: #8a8b8d;
}
/*.scenario-name > #hint {*/
/*color: #8a8b8d;*/
/*}*/
.scenario-btn {
text-align: center;
@ -241,4 +259,9 @@ export default {
.scenario-draggable {
background-color: #909399;
}
.disable-scenario >>> .el-collapse-item__header {
border-right: 2px solid #909399;
color: #8a8b8d;
}
</style>

View File

@ -113,6 +113,7 @@
<style scoped>
.el-collapse-item__header {
padding-left: 7px;
border-right: 2px solid #409eff;
}
.el-collapse-item__header.is-active {
@ -122,4 +123,5 @@
.el-collapse-item__content {
padding-bottom: 0;
}
</style>

View File

@ -11,7 +11,7 @@
</el-select>
</el-form-item>
<el-button class="debug-button" size="small" type="primary" @click="runDebug">{{$t('load_test.save_and_run')}}</el-button>
<el-button :disabled="!request.enable || !scenario.enable || isReadOnly" class="debug-button" size="small" type="primary" @click="runDebug">{{$t('load_test.save_and_run')}}</el-button>
<el-tabs v-model="activeName">
<el-tab-pane label="Interface" name="interface">
@ -58,7 +58,7 @@
import MsApiKeyValue from "../ApiKeyValue";
import MsApiBody from "../ApiBody";
import MsApiAssertions from "../assertion/ApiAssertions";
import {DubboRequest} from "../../model/ScenarioModel";
import {DubboRequest, Scenario} from "../../model/ScenarioModel";
import MsApiExtract from "../extract/ApiExtract";
import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
import MsDubboInterface from "@/business/components/api/test/components/request/dubbo/Interface";
@ -78,6 +78,7 @@
},
props: {
request: DubboRequest,
scenario: Scenario,
isReadOnly: {
type: Boolean,
default: false

View File

@ -40,8 +40,7 @@
</el-switch>
</el-form-item>
<el-button class="debug-button" size="small" type="primary" @click="runDebug">{{ $t('load_test.save_and_run') }}
</el-button>
<el-button :disabled="!request.enable || !scenario.enable || isReadOnly" class="debug-button" size="small" type="primary" @click="runDebug">{{ $t('load_test.save_and_run') }}</el-button>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
@ -74,6 +73,9 @@
<el-tab-pane :label="$t('api_test.request.processor.post_exec_script')" name="beanShellPostProcessor">
<ms-bean-shell-processor :is-read-only="isReadOnly" :bean-shell-processor="request.beanShellPostProcessor"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.timeout_config')" name="advancedConfig">
<ms-api-advanced-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
</el-tabs>
</el-form>
</template>
@ -88,10 +90,12 @@ import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
import {REQUEST_HEADERS} from "@/common/js/constants";
import MsApiVariable from "@/business/components/api/test/components/ApiVariable";
import MsBeanShellProcessor from "../processor/BeanShellProcessor";
import MsApiAdvancedConfig from "../ApiAdvancedConfig";
export default {
name: "MsApiHttpRequestForm",
components: {
MsApiAdvancedConfig,
MsBeanShellProcessor,
MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
props: {

View File

@ -2,7 +2,7 @@
<div class="request-container">
<draggable :list="this.scenario.requests" group="Request" class="request-draggable" ghost-class="request-ghost">
<div class="request-item" v-for="(request, index) in this.scenario.requests" :key="index" @click="select(request)"
:class="{'selected': isSelected(request)}">
:class="{'selected': isSelected(request), 'disable-request': !request.enable || !scenario.enable}">
<el-row type="flex" align="middle">
<div class="request-type">
{{request.showType()}}
@ -23,6 +23,12 @@
<el-dropdown-item :disabled="isReadOnly" :command="{type: 'delete', index: index}">
{{$t('api_test.request.delete')}}
</el-dropdown-item>
<el-dropdown-item v-if="request.enable" :disabled="isReadOnly" :command="{type: 'disable', index: index}">
{{$t('api_test.scenario.disable')}}
</el-dropdown-item>
<el-dropdown-item v-if="!request.enable" :disabled="isReadOnly" :command="{type: 'enable', index: index}">
{{$t('api_test.scenario.enable')}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
@ -70,7 +76,7 @@
computed: {
isSelected() {
return function (request) {
return this.selected.id === request.id;
return this.selected === request;
}
}
},
@ -89,6 +95,12 @@
let request = this.scenario.requests[index];
this.scenario.requests.push(new RequestFactory(request));
},
disableRequest: function (index) {
this.scenario.requests[index].enable = false;
},
enableRequest: function (index) {
this.scenario.requests[index].enable = true;
},
deleteRequest: function (index) {
this.scenario.requests.splice(index, 1);
if (this.scenario.requests.length === 0) {
@ -103,6 +115,12 @@
case "delete":
this.deleteRequest(command.index);
break;
case "disable":
this.disableRequest(command.index);
break;
case "enable":
this.enableRequest(command.index);
break;
}
},
select: function (request) {
@ -186,4 +204,17 @@
opacity: 0.5;
background-color: #909399;
}
.request-item.disable-request {
border-left-color: #909399;
}
.disable-request .request-type {
background-color: #909399;
}
.disable-request .request-method {
color: #909399;
}
</style>

View File

@ -1,7 +1,8 @@
<template>
<div class="request-form">
<component @runDebug="runDebug" :is="component" :is-read-only="isReadOnly" :request="request" :scenario="scenario"/>
<ms-request-result-tail v-loading="debugReportLoading" v-if="isCompleted" :request="request.debugRequestResult ? request.debugRequestResult : {responseResult: {}, subRequestResults: []}" :scenario-name="request.debugScenario ? request.debugScenario.name : ''"/>
<ms-request-result-tail v-loading="debugReportLoading" v-if="isCompleted" :request="request.debugRequestResult ? request.debugRequestResult : {responseResult: {}, subRequestResults: []}"
:scenario-name="request.debugScenario ? request.debugScenario.name : ''" ref="msDebugResult"/>
</div>
</template>
@ -72,9 +73,15 @@ export default {
if (res) {
this.debugReportLoading = false;
this.request.debugReport = res;
if (res.scenarios && res.scenarios.length > 0) {
this.request.debugScenario = res.scenarios[0];
this.request.debugRequestResult = this.request.debugScenario.requestResults[0];
this.deleteReport(this.debugReportId)
this.deleteReport(this.debugReportId);
} else {
this.request.debugScenario = new Scenario();
this.request.debugRequestResult = {responseResult: {}, subRequestResults: []};
}
this.$refs.msDebugResult.reload();
} else {
setTimeout(this.getReport, 2000)
}

View File

@ -289,6 +289,12 @@ export class HTTPSamplerProxy extends DefaultTestElement {
} else {
this.stringProp("HTTPSampler.port", options.port);
}
if (options.connectTimeout) {
this.stringProp('HTTPSampler.connect_timeout', options.connectTimeout);
}
if (options.responseTimeout) {
this.stringProp('HTTPSampler.response_timeout', options.responseTimeout);
}
this.boolProp("HTTPSampler.follow_redirects", options.follow, true);
this.boolProp("HTTPSampler.use_keepalive", options.keepalive, true);

View File

@ -207,6 +207,7 @@ export class Scenario extends BaseConfig {
this.dubboConfig = undefined;
this.environment = undefined;
this.enableCookieShare = false;
this.enable = true;
this.set(options);
this.sets({variables: KeyValue, headers: KeyValue, requests: RequestFactory}, options);
@ -304,6 +305,9 @@ export class HttpRequest extends Request {
this.debugReport = undefined;
this.beanShellPreProcessor = undefined;
this.beanShellPostProcessor = undefined;
this.enable = true;
this.connectTimeout = 60*1000;
this.responseTimeout = undefined;
this.set(options);
this.sets({parameters: KeyValue, headers: KeyValue}, options);
@ -383,6 +387,7 @@ export class DubboRequest extends Request {
this.debugReport = undefined;
this.beanShellPreProcessor = new BeanShellProcessor(options.beanShellPreProcessor);
this.beanShellPostProcessor = new BeanShellProcessor(options.beanShellPostProcessor);
this.enable = true;
this.sets({args: KeyValue, attachmentArgs: KeyValue}, options);
}
@ -723,6 +728,9 @@ class JMXHttpRequest {
let url = new URL(environment.protocol + "://" + environment.socket);
this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname + (request.path ? request.path : '')));
}
this.connectTimeout = request.connectTimeout;
this.responseTimeout = request.responseTimeout;
}
}
@ -807,6 +815,8 @@ class JMXGenerator {
addScenarios(testPlan, scenarios) {
scenarios.forEach(s => {
if (s.enable) {
let scenario = s.clone();
let threadGroup = new ThreadGroup(scenario.name || "");
@ -818,6 +828,7 @@ class JMXGenerator {
this.addScenarioCookieManager(threadGroup, scenario);
scenario.requests.forEach(request => {
if (request.enable) {
if (!request.isValid()) return;
let sampler;
@ -842,9 +853,12 @@ class JMXGenerator {
this.addRequestExtractor(sampler, request);
threadGroup.put(sampler);
}
})
testPlan.put(threadGroup);
}
})
}

View File

@ -0,0 +1,7 @@
import Vue from 'vue'
export const LIST_CHANGE = 'LIST_CHANGE';
export let ApiEvent = new Vue();
export let TrackEvent = new Vue();
export let PerformanceEvent = new Vue();

View File

@ -10,25 +10,27 @@
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
index="3" popper-class="submenu">
<template v-slot:title>{{ $t('commons.project') }}</template>
<ms-recent-list :options="projectRecent"/>
<ms-recent-list ref="projectRecent" :options="projectRecent"/>
<el-divider/>
<ms-show-all :index="'/performance/project/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/project/create'" :title="$t('project.create')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/project/create'"
:title="$t('project.create')"/>
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
index="4" popper-class="submenu">
<template v-slot:title>{{ $t('commons.test') }}</template>
<ms-recent-list :options="testRecent"/>
<ms-recent-list ref="testRecent" :options="testRecent"/>
<el-divider/>
<ms-show-all :index="'/performance/test/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/test/create'" :title="$t('load_test.create')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/test/create'"
:title="$t('load_test.create')"/>
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
index="5" popper-class="submenu">
<template v-slot:title>{{ $t('commons.report') }}</template>
<ms-recent-list :options="reportRecent"/>
<ms-recent-list ref="reportRecent" :options="reportRecent"/>
<el-divider/>
<ms-show-all :index="'/performance/report/all'"/>
</el-submenu>
@ -50,6 +52,7 @@
import MsRecentList from "../../common/head/RecentList";
import MsCreateButton from "../../common/head/CreateButton";
import MsShowAll from "../../common/head/ShowAll";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "PerformanceHeaderMenus",
@ -92,6 +95,13 @@
}
}
},
mounted() {
PerformanceEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.testRecent.recent();
this.$refs.reportRecent.recent();
});
}
}
</script>

View File

@ -45,7 +45,8 @@
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="triggerMode" width="150" :label="'触发方式'" column-key="triggerMode" :filters="triggerFilters">
<el-table-column prop="triggerMode" width="150" :label="'触发方式'" column-key="triggerMode"
:filters="triggerFilters">
<template v-slot:default="scope">
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
</template>
@ -63,8 +64,10 @@
width="150"
:label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator-button :tip="$t('api_report.detail')" icon="el-icon-s-data" @exec="handleEdit(scope.row)" type="primary"/>
<ms-table-operator-button :is-tester-permission="true" :tip="$t('api_report.delete')" icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger"/>
<ms-table-operator-button :tip="$t('api_report.detail')" icon="el-icon-s-data"
@exec="handleEdit(scope.row)" type="primary"/>
<ms-table-operator-button :is-tester-permission="true" :tip="$t('api_report.delete')"
icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger"/>
</template>
</el-table-column>
</el-table>
@ -85,13 +88,15 @@
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../common/components/search/search-components";
import MsTableHeader from "../../common/components/MsTableHeader";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "PerformanceTestReport",
components: {
MsTableHeader,
ReportTriggerModeItem,
MsTableOperatorButton, MsPerformanceReportStatus, MsTablePagination, MsContainer, MsMainContainer},
MsTableOperatorButton, MsPerformanceReportStatus, MsTablePagination, MsContainer, MsMainContainer
},
created: function () {
this.initTableData();
},
@ -177,6 +182,8 @@
this.result = this.$post(this.deletePath + report.id, {}, () => {
this.$success(this.$t('commons.delete_success'));
this.initTableData();
// 广 head
PerformanceEvent.$emit(LIST_CHANGE);
});
},
sort(column) {

View File

@ -83,6 +83,7 @@
import {_filter, _sort} from "../../../../common/js/utils";
import MsTableHeader from "../../common/components/MsTableHeader";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
export default {
components: {
@ -192,6 +193,8 @@
this.result = this.$post(this.deletePath, data, () => {
this.$success(this.$t('commons.delete_success'));
this.initTableData();
// 广 head
PerformanceEvent.$emit(LIST_CHANGE);
});
},
sort(column) {

View File

@ -34,7 +34,8 @@
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator :is-tester-permission="true" @editClick="edit(scope.row)" @deleteClick="handleDelete(scope.row)">
<ms-table-operator :is-tester-permission="true" @editClick="edit(scope.row)"
@deleteClick="handleDelete(scope.row)">
<template v-if="baseUrl == 'api'" v-slot:behind>
<ms-table-operator-button :is-tester-permission="true" :tip="'环境配置'" icon="el-icon-setting"
type="info" @exec="openEnvironmentConfig(scope.row)"/>
@ -56,10 +57,10 @@
<el-form-item :label="$t('commons.description')" prop="description">
<el-input :autosize="{ minRows: 2, maxRows: 4}" type="textarea" v-model="form.description"></el-input>
</el-form-item>
<el-form-item label="TAPD项目ID" v-if="tapd">
<el-form-item :label="$t('project.tapd_id')" v-if="tapd">
<el-input v-model="form.tapdId" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="JIRA项目key" v-if="jira">
<el-form-item :label="$t('project.jira_key')" v-if="jira">
<el-input v-model="form.jiraKey" autocomplete="off"></el-input>
</el-form-item>
</el-form>
@ -93,6 +94,7 @@
import MsTableOperatorButton from "../common/components/MsTableOperatorButton";
import ApiEnvironmentConfig from "../api/test/components/ApiEnvironmentConfig";
import TemplateComponent from "../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import {ApiEvent, LIST_CHANGE, PerformanceEvent, TrackEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "MsProject",
@ -102,7 +104,8 @@
MsTableOperatorButton,
MsDeleteConfirm,
MsMainContainer,
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter},
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter
},
data() {
return {
createVisible: false,
@ -223,6 +226,10 @@
this.$get('/project/delete/' + project.id, () => {
Message.success(this.$t('commons.delete_success'));
this.list();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
TrackEvent.$emit(LIST_CHANGE);
PerformanceEvent.$emit(LIST_CHANGE);
});
}).catch(() => {
this.$message({

View File

@ -1,5 +1,5 @@
<template>
<el-card class="header-title" v-loading="result.loading">
<div class="header-title" v-loading="result.loading">
<div>
<div>{{$t('organization.integration.select_defect_platform')}}</div>
<el-radio-group v-model="platform" style="margin-top: 10px" @change="change">
@ -33,7 +33,8 @@
</el-button>
<el-button v-if="showEdit" size="mini" @click="edit">{{$t('commons.edit')}}</el-button>
<el-button type="primary" v-if="showSave" size="mini" @click="save('form')">{{$t('commons.save')}}</el-button>
<el-button v-if="showCancel" size="mini" @click="cancelEdit">{{$t('organization.integration.cancel_edit')}}</el-button>
<el-button v-if="showCancel" size="mini" @click="cancelEdit">{{$t('organization.integration.cancel_edit')}}
</el-button>
<el-button type="info" size="mini" @click="cancelIntegration('form')" :disabled="!show">
{{$t('organization.integration.cancel_integration')}}
</el-button>
@ -46,11 +47,12 @@
</div>
<div>
2. {{$t('organization.integration.use_tip_two')}}
<router-link to="/track/project/all" style="margin-left: 5px">{{$t('organization.integration.link_the_project_now')}}
<router-link to="/track/project/all" style="margin-left: 5px">
{{$t('organization.integration.link_the_project_now')}}
</router-link>
</div>
</div>
</el-card>
</div>
</template>
<script>
@ -69,9 +71,21 @@
showSave: false,
showCancel: false,
rules: {
account: {required: true, message: this.$t('organization.integration.input_api_account'), trigger: ['change', 'blur']},
password: {required: true, message: this.$t('organization.integration.input_api_password'), trigger: ['change', 'blur']},
url: {required: true, message: this.$t('organization.integration.input_jira_url'), trigger: ['change', 'blur']}
account: {
required: true,
message: this.$t('organization.integration.input_api_account'),
trigger: ['change', 'blur']
},
password: {
required: true,
message: this.$t('organization.integration.input_api_password'),
trigger: ['change', 'blur']
},
url: {
required: true,
message: this.$t('organization.integration.input_jira_url'),
trigger: ['change', 'blur']
}
},
}
},
@ -188,9 +202,14 @@
});
},
testConnection() {
if (this.form.account && this.form.password && this.platform) {
this.result = this.$get("issues/auth/" + this.platform, () => {
this.$success(this.$t('organization.integration.verified'));
});
} else {
this.$warning(this.$t('organization.integration.not_integrated'));
return false;
}
}
}
}

View File

@ -1,11 +1,11 @@
<template>
<div>
<el-card>
<el-tabs class="system-setting" v-model="activeName">
<el-tab-pane :label="$t('organization.defect_manage')" name="defect">
<defect-management/>
</el-tab-pane>
</el-tabs>
</div>
</el-card>
</template>
<script>

View File

@ -16,10 +16,22 @@
</template>
<el-table border class="adjust-table" :data="tableData" style="width: 100%">
<el-table-column prop="accessKey" label="Access Key"/>
<el-table-column prop="accessKey" label="Access Key">
<template v-slot:default="scope">
<div class="variable-combine">
<div class="variable">{{ scope.row.accessKey }}</div>
<div>
<el-tooltip :content="$t('api_test.copied')" manual v-model="scope.row.visible" placement="top"
:visible-arrow="false">
<i class="el-icon-copy-document copy" @click="copy(scope.row, 'accessKey', 'visible')"/>
</el-tooltip>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="secretKey" label="Secret Key">
<template v-slot:default="scope">
<el-link type="info" @click="showSecretKey(scope.row)">{{$t('commons.show')}}</el-link>
<el-link type="primary" @click="showSecretKey(scope.row)">{{ $t('commons.show') }}</el-link>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')">
@ -46,7 +58,15 @@
</el-table-column>
</el-table>
</el-card>
<el-dialog title="Secret Key" :visible.sync="apiKeysVisible">
<div class="variable">
{{ currentRow.secretKey }}
<el-tooltip :content="$t('api_test.copied')" manual v-model="currentRow.visible2" placement="top"
:visible-arrow="false">
<i class="el-icon-copy-document copy" @click="copy(currentRow, 'secretKey', 'visible2')"/>
</el-tooltip>
</div>
</el-dialog>
</div>
</template>
@ -67,6 +87,7 @@
apiKeysVisible: false,
condition: {},
tableData: [],
currentRow: {},
}
},
@ -121,12 +142,50 @@
}
},
showSecretKey(row) {
this.$alert(row.secretKey, 'Secret Key');
this.apiKeysVisible = true;
this.currentRow = row;
},
copy(row, key, visible) {
let input = document.createElement("input");
document.body.appendChild(input);
input.value = row[key];
input.select();
if (input.setSelectionRange) {
input.setSelectionRange(0, input.value.length);
}
document.execCommand("copy");
document.body.removeChild(input);
row[visible] = true;
setTimeout(() => {
row[visible] = false;
}, 1000);
},
}
}
</script>
<style scoped>
.variable-combine {
color: #7F7F7F;
line-height: 32px;
position: absolute;
top: 10px;
margin-right: -20px;
display: flex;
align-items: center;
}
.variable {
display: inline-block;
margin-right: 10px;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.copy {
font-size: 14px;
cursor: pointer;
color: #1E90FF;
}
</style>

View File

@ -1,6 +1,5 @@
<template>
<div>
<el-card class="box-card" v-loading="result.loading">
<div v-loading="result.loading">
<!--邮件表单-->
<el-form :model="formInline" :rules="rules" ref="formInline" class="demo-form-inline"
:disabled="show" v-loading="loading" size="small">
@ -61,7 +60,6 @@
</el-button>
<el-button @click="cancel" type="info" v-if="showCancel" size="small">{{ $t('commons.cancel') }}</el-button>
</div>
</el-card>
</div>
</template>
@ -71,8 +69,7 @@
name: "EmailSetting",
data() {
return {
formInline: {
},
formInline: {},
input: '',
visible: true,
result: {},

View File

@ -1,6 +1,5 @@
<template>
<div>
<el-card class="box-card" v-loading="result.loading">
<div v-loading="result.loading">
<el-form :model="form" size="small" :rules="rules" :disabled="show" ref="form">
<el-form-item :label="$t('ldap.url')" prop="url">
<el-input v-model="form.url" :placeholder="$t('ldap.input_url_placeholder')"/>
@ -33,7 +32,8 @@
{{ $t('ldap.test_login') }}
</el-button>
<el-button v-if="showEdit" size="small" @click="edit">{{ $t('ldap.edit') }}</el-button>
<el-button type="success" v-if="showSave" size="small" @click="save('form')">{{$t('commons.save')}}</el-button>
<el-button type="success" v-if="showSave" size="small" @click="save('form')">{{ $t('commons.save') }}
</el-button>
<el-button type="info" v-if="showCancel" size="small" @click="cancel">{{ $t('commons.cancel') }}</el-button>
</div>
@ -55,7 +55,6 @@
</span>
</el-dialog>
</el-card>
</div>
</template>

View File

@ -1,5 +1,5 @@
<template>
<div>
<el-card>
<el-tabs class="system-setting" v-model="activeName">
<el-tab-pane :label="$t('system_parameter_setting.mailbox_service_settings')" name="email">
<email-setting/>
@ -8,12 +8,13 @@
<ldap-setting/>
</el-tab-pane>
</el-tabs>
</div>
</el-card>
</template>
<script>
import EmailSetting from "./EmailSetting";
import LdapSetting from "./LdapSetting";
export default {
name: "SystemParameterSetting",
components: {

View File

@ -14,7 +14,7 @@
<el-option v-for="(type, index) in typeArr" :key="index" :value="type.id" :label="type.name"/>
</el-select>
</el-form-item>
<el-form-item label="更新后属性值为" prop="value">
<el-form-item :label="$t('test_track.case.updated_attr_value')" prop="value">
<el-select v-model="form.value" style="width: 80%" :filterable="filterable">
<el-option v-for="(option, index) in options" :key="index" :value="option.id" :label="option.name">
<div v-if="option.email">
@ -47,7 +47,9 @@
valueArr: Object,
dialogTitle: {
type: String,
default: "批量操作"
default() {
return this.$t('test_track.case.batch_operate')
}
}
},
data() {
@ -56,8 +58,8 @@
form: {},
size: 0,
rules: {
type: {required: true, message: "请选择属性", trigger: ['blur','change']},
value: {required: true, message: "请选择属性对应的值", trigger: ['blur','change']}
type: {required: true, message: this.$t('test_track.case.please_select_attr'), trigger: ['blur','change']},
value: {required: true, message: this.$t('test_track.case.please_select_attr_value'), trigger: ['blur','change']}
},
options: [],
filterable: false,

View File

@ -1,6 +1,6 @@
<template>
<div v-loading="result.loading">
<el-dialog title="选择用例目录"
<el-dialog :title="this.$t('test_track.case.select_catalog')"
:visible.sync="dialogVisible"
:before-close="close"
:destroy-on-close="true"

View File

@ -121,7 +121,7 @@
</el-card>
<batch-edit ref="batchEdit" @batchEdit="batchEdit"
:typeArr="typeArr" :value-arr="valueArr" dialog-title="批量编辑用例"/>
:typeArr="typeArr" :value-arr="valueArr" :dialog-title="$t('test_track.case.batch_edit_case')"/>
</div>
</template>
@ -144,6 +144,7 @@
import ShowMoreBtn from "./ShowMoreBtn";
import BatchEdit from "./BatchEdit";
import {WORKSPACE_ID} from "../../../../../common/js/constants";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "TestCaseList",
@ -193,18 +194,18 @@
showMore: false,
buttons: [
{
name: '批量编辑用例', handleClick: this.handleBatchEdit
name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleBatchEdit
}, {
name: '批量移动用例', handleClick: this.handleBatchMove
name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove
}, {
name: '批量删除用例', handleClick: this.handleDeleteBatch
name: this.$t('test_track.case.batch_delete_case'), handleClick: this.handleDeleteBatch
}
],
typeArr: [
{id: 'priority', name: '用例等级'},
{id: 'type', name: '类型'},
{id: 'method', name: '测试方式'},
{id: 'maintainer', name: '维护人'},
{id: 'priority', name: this.$t('test_track.case.priority')},
{id: 'type', name: this.$t('test_track.case.type')},
{id: 'method', name: this.$t('test_track.case.method')},
{id: 'maintainer', name: this.$t('test_track.case.maintainer')},
],
valueArr: {
priority: [
@ -304,6 +305,8 @@
this.selectRows.clear();
this.$emit("refresh");
this.$success(this.$t('commons.delete_success'));
// 广 head
TrackEvent.$emit(LIST_CHANGE);
});
}
}
@ -314,6 +317,8 @@
this.$post('/test/case/delete/' + testCaseId, {}, () => {
this.initTableData();
this.$success(this.$t('commons.delete_success'));
// 广 head
TrackEvent.$emit(LIST_CHANGE);
});
},
refresh() {

View File

@ -11,30 +11,33 @@
<el-submenu :class="{'deactivation':!isProjectActivation}"
v-permission="['test_manager','test_user','test_viewer']" index="3" popper-class="submenu">
<template v-slot:title>{{ $t('commons.project') }}</template>
<ms-recent-list :options="projectRecent"/>
<ms-recent-list ref="projectRecent" :options="projectRecent"/>
<el-divider/>
<ms-show-all :index="'/track/project/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/project/create'" :title="$t('project.create')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/project/create'"
:title="$t('project.create')"/>
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
index="6" popper-class="submenu">
<template v-slot:title>{{ $t('test_track.case.test_case') }}</template>
<ms-recent-list :options="caseRecent"/>
<ms-recent-list ref="caseRecent" :options="caseRecent"/>
<el-divider/>
<ms-show-all :index="'/track/case/all'"/>
<el-menu-item :index="testCaseEditPath" class="blank_item"></el-menu-item>
<el-menu-item :index="testCaseProjectPath" class="blank_item"></el-menu-item>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/case/create'" :title="$t('test_track.case.create_case')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/case/create'"
:title="$t('test_track.case.create_case')"/>
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="7" popper-class="submenu">
<template v-slot:title>{{ $t('test_track.plan.test_plan') }}</template>
<ms-recent-list :options="planRecent"/>
<ms-recent-list ref="planRecent" :options="planRecent"/>
<el-divider/>
<ms-show-all :index="'/track/plan/all'"/>
<el-menu-item :index="testPlanViewPath" class="blank_item"></el-menu-item>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/plan/create'" :title="$t('test_track.plan.create_plan')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/plan/create'"
:title="$t('test_track.plan.create_plan')"/>
</el-submenu>
</el-menu>
</el-col>
@ -48,6 +51,7 @@
import MsShowAll from "../../common/head/ShowAll";
import MsRecentList from "../../common/head/RecentList";
import MsCreateButton from "../../common/head/CreateButton";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "TrackHeaderMenus",
@ -75,7 +79,8 @@
index: function (item) {
return '/track/case/edit/' + item.id;
},
router: function (item) {}
router: function (item) {
}
},
planRecent: {
title: this.$t('test_track.recent_plan'),
@ -83,7 +88,8 @@
index: function (item) {
return '/track/plan/view/' + item.id;
},
router: function (item) {}
router: function (item) {
}
}
}
},
@ -94,6 +100,11 @@
},
mounted() {
this.init();
TrackEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.planRecent.recent();
this.$refs.caseRecent.recent();
});
},
methods: {
reload() {

View File

@ -40,10 +40,12 @@
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Prepare'}">
{{ $t('test_track.plan.plan_status_prepare') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Underway'}">
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
:command="{id: scope.row.id, status: 'Underway'}">
{{ $t('test_track.plan.plan_status_running') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Completed'}">
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
:command="{id: scope.row.id, status: 'Completed'}">
{{ $t('test_track.plan.plan_status_completed') }}
</el-dropdown-item>
</el-dropdown-menu>
@ -87,10 +89,15 @@
<el-table-column
:label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)" @deleteClick="handleDelete(scope.row)">
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)"
@deleteClick="handleDelete(scope.row)">
<template v-slot:middle>
<ms-table-operator-button type="success" v-if="!scope.row.reportId" :tip="$t('test_track.plan_view.create_report')" icon="el-icon-document" @exec="openTestReportTemplate(scope.row)"/>
<ms-table-operator-button type="success" v-if="scope.row.reportId" :tip="$t('test_track.plan_view.view_report')" icon="el-icon-document" @exec="openReport(scope.row.id, scope.row.reportId)"/>
<ms-table-operator-button :isTesterPermission="true" type="success" v-if="!scope.row.reportId"
:tip="$t('test_track.plan_view.create_report')" icon="el-icon-document"
@exec="openTestReportTemplate(scope.row)"/>
<ms-table-operator-button type="success" v-if="scope.row.reportId"
:tip="$t('test_track.plan_view.view_report')" icon="el-icon-document"
@exec="openReport(scope.row.id, scope.row.reportId)"/>
</template>
</ms-table-operator>
</template>
@ -121,6 +128,7 @@
import TestCaseReportView from "../view/comonents/report/TestCaseReportView";
import MsDeleteConfirm from "../../../common/components/MsDeleteConfirm";
import {TEST_PLAN_CONFIGS} from "../../../common/components/search/search-components";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "TestPlanList",
@ -130,7 +138,8 @@
TestReportTemplateList,
PlanStageTableItem,
PlanStatusTableItem,
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination},
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination
},
data() {
return {
result: {},
@ -211,6 +220,8 @@
this.$post('/test/plan/delete/' + testPlanId, {}, () => {
this.initTableData();
this.$success(this.$t('commons.delete_success'));
// 广 head
TrackEvent.$emit(LIST_CHANGE);
});
},
intoPlan(row, event, column) {

View File

@ -181,7 +181,7 @@
:active-text="$t('test_track.plan_view.submit_issues')">
</el-switch>
<el-tooltip class="item" effect="dark"
content="在系统设置-组织-服务集成中集成缺陷管理平台可以自动提交缺陷到指定缺陷管理平台"
:content="$t('test_track.issue.platform_tip')"
placement="right">
<i class="el-icon-info"/>
</el-tooltip>
@ -192,7 +192,7 @@
<el-col :span="20" :offset="1" class="issues-edit">
<el-input
type="text"
placeholder="请输入标题"
:placeholder="$t('test_track.issue.input_title')"
v-model="testCase.issues.title"
maxlength="100"
show-word-limit
@ -207,14 +207,28 @@
<el-row>
<el-col :span="20" :offset="1" class="issues-edit">
<el-table border class="adjust-table" :data="issues" style="width: 100%">
<el-table-column prop="id" label="缺陷ID" show-overflow-tooltip/>
<el-table-column prop="title" label="缺陷标题"/>
<el-table-column prop="description" label="缺陷描述" show-overflow-tooltip/>
<el-table-column prop="status" label="缺陷状态"/>
<el-table-column prop="platform" label="平台"/>
<el-table-column label="操作">
<el-table-column prop="id" :label="$t('test_track.issue.id')" show-overflow-tooltip/>
<el-table-column prop="title" :label="$t('test_track.issue.title')"/>
<el-table-column prop="description" :label="$t('test_track.issue.description')">
<template v-slot:default="scope">
<el-tooltip content="关闭缺陷"
<el-popover
placement="left"
width="400"
trigger="hover"
>
<ckeditor :editor="editor" disabled
v-model="scope.row.description"/>
<!-- <span v-html="scope.row.description"/>-->
<!-- <span slot="reference">{{scope.row.description}}</span>-->
<el-button slot="reference" type="text">预览</el-button>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('test_track.issue.status')"/>
<el-table-column prop="platform" :label="$t('test_track.issue.platform')"/>
<el-table-column :label="$t('test_track.issue.operate')">
<template v-slot:default="scope">
<el-tooltip :content="$t('test_track.issue.close')"
placement="right">
<el-button type="danger" icon="el-icon-circle-close" size="mini"
circle v-if="scope.row.platform === 'Local'"
@ -473,7 +487,7 @@
},
saveIssues() {
if (!this.testCase.issues.title || !this.testCase.issues.content) {
this.$warning("标题和描述必填");
this.$warning(this.$t('test_track.issue.title_description_required'));
return;
}
let param = {};
@ -494,7 +508,7 @@
closeIssue(row) {
this.result = this.$get("/issues/close/" + row.id, () => {
this.getIssues(this.testCase.caseId);
this.$success("关闭成功");
this.$success(this.$t('test_track.issue.close_success'));
});
}
}

View File

@ -104,7 +104,7 @@
<el-table-column
prop="nodePath"
label="缺陷"
:label="$t('test_track.issue.issue')"
show-overflow-tooltip>
<template v-slot:default="scope">
<el-popover
@ -113,10 +113,24 @@
trigger="hover">
<el-table border class="adjust-table" :data="scope.row.issuesContent" style="width: 100%">
<!-- <el-table-column prop="id" label="缺陷ID" show-overflow-tooltip/>-->
<el-table-column prop="title" label="缺陷标题"/>
<el-table-column prop="description" label="缺陷描述" show-overflow-tooltip/>
<el-table-column prop="title" :label="$t('test_track.issue.title')"/>
<el-table-column prop="description" :label="$t('test_track.issue.description')">
<template v-slot:default="scope">
<el-popover
placement="left"
width="400"
trigger="hover"
>
<ckeditor :editor="editor" disabled
v-model="scope.row.description"/>
<!-- <span v-html="scope.row.description"/>-->
<!-- <span slot="reference">{{scope.row.description}}</span>-->
<el-button slot="reference" type="text">预览</el-button>
</el-popover>
</template>
</el-table-column>
<!-- <el-table-column prop="status" label="缺陷状态"/>-->
<el-table-column prop="platform" label="平台"/>
<el-table-column prop="platform" :label="$t('test_track.issue.platform')"/>
</el-table>
<el-button slot="reference" type="text">{{scope.row.issuesSize}}</el-button>
</el-popover>
@ -195,7 +209,7 @@
<test-case-report-view @refresh="initTableData" ref="testCaseReportView"/>
</el-card>
<batch-edit ref="batchEdit" @batchEdit="batchEdit"
:type-arr="typeArr" :value-arr="valueArr" dialog-title="批量更改测试计划"/>
:type-arr="typeArr" :value-arr="valueArr" :dialog-title="$t('test_track.case.batch_edit_plan')"/>
</div>
</template>
@ -222,6 +236,7 @@
import {TEST_CASE_CONFIGS} from "../../../../common/components/search/search-components";
import ShowMoreBtn from "../../../case/components/ShowMoreBtn";
import BatchEdit from "../../../case/components/BatchEdit";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
export default {
name: "TestPlanTestCaseList",
@ -280,15 +295,15 @@
showMore: false,
buttons: [
{
name: '批量更改测试计划', handleClick: this.handleBatchEdit
name: this.$t('test_track.case.batch_edit_plan'), handleClick: this.handleBatchEdit
},
{
name: '批量取消用例关联', handleClick: this.handleDeleteBatch
name: this.$t('test_track.case.batch_unlink'), handleClick: this.handleDeleteBatch
}
],
typeArr: [
{id: 'status', name: '执行结果'},
{id: 'executor', name: '执行人'},
{id: 'status', name: this.$t('test_track.plan_view.execute_result')},
{id: 'executor', name: this.$t('test_track.plan_view.executor')},
],
valueArr: {
executor: [],
@ -298,7 +313,8 @@
{name: this.$t('test_track.plan_view.blocking'), id: 'Blocking'},
{name: this.$t('test_track.plan_view.skip'), id: 'Skip'}
]
}
},
editor: ClassicEditor,
}
},
props: {

@ -1 +1 @@
Subproject commit 06fc0a321a9886419be5c607ddaa6b40efb5179b
Subproject commit 7e4d80cc2b870a8cac6dbb9fe6711ab6041faf6d

View File

@ -107,6 +107,7 @@ export default {
please_save: 'Please save first',
formatErr: 'Format Error',
id: 'ID',
millisecond: 'ms',
please_upload: 'Please upload file',
reference_documentation: "Reference documentation",
date: {
@ -213,6 +214,8 @@ export default {
owning_workspace: 'Owning Workspace',
please_choose_workspace: 'Please select Workspace',
special_characters_are_not_supported: 'Incorrect format (special characters are not supported and cannot end with \'-\')',
tapd_id: 'TAPD Project ID',
jira_key: 'JIRA Project key',
},
member: {
create: 'Create',
@ -397,7 +400,9 @@ export default {
headers: "Headers",
kv_description: "Variables are available for all requests",
copy: "Copy scenario",
delete: "Delete scenario"
delete: "Delete scenario",
disable: "Disable",
enable: "Enable"
},
request: {
copy: "Copy request",
@ -433,6 +438,9 @@ export default {
body: "Body",
body_kv: "Key Value",
body_text: "Raw",
timeout_config: "Timeout Config",
connect_timeout: "Connect Timeout",
response_timeout: "Response Timeout",
assertions: {
label: "Assertion",
text: "Text",
@ -587,6 +595,16 @@ export default {
relate_test_not_find: 'The associated test does not exist, please check the test case',
batch_handle: 'Batch processing (select {0} item)',
batch_update: 'Update the attributes of {0} cases',
select_catalog: 'Please select use case catalog',
updated_attr_value: 'The updated attribute value',
batch_operate: 'Batch operation',
please_select_attr: 'Please select attributes',
please_select_attr_value: 'Please select the value corresponding to the attribute',
batch_edit_plan: 'Batch change test plan',
batch_edit_case: 'Batch editing test cases',
batch_move_case: 'Batch move',
batch_delete_case: 'Batch delete',
batch_unlink: 'Batch Unlink',
import: {
import: "Import test case",
case_import: "Import test case",
@ -697,6 +715,20 @@ export default {
test_detail: "Test detail",
failure_case: "Failure case",
export_report: "Export Report"
},
issue: {
issue: "Issue",
platform_tip: "Integrated defect management platform in the system setting-organization-service integration can automatically submit defects to the designated defect management platform",
input_title: "Please enter title",
id: "Issue ID",
title: "Issue Title",
description: "Issue Description",
status: "Issue Status",
platform: "Platform",
operate: "Operate",
close: "Close",
title_description_required: "Title and description are required",
close_success: "Closed successfully",
}
},
test_resource_pool: {

View File

@ -109,6 +109,7 @@ export default {
please_save: '请先保存',
reference_documentation: "参考文档",
id: 'ID',
millisecond: '毫秒',
date: {
select_date: '选择日期',
start_date: '开始日期',
@ -213,6 +214,8 @@ export default {
owning_workspace: '所属工作空间',
please_choose_workspace: '请选择工作空间',
special_characters_are_not_supported: '格式错误(不支持特殊字符,且不能以\'-\'开头结尾)',
tapd_id: 'TAPD项目ID',
jira_key: 'JIRA项目key',
},
member: {
create: '添加成员',
@ -397,7 +400,9 @@ export default {
headers: "请求头",
kv_description: "所有请求可以使用自定义变量",
copy: "复制场景",
delete: "删除场景"
delete: "删除场景",
disable: "禁用",
enable: "启用"
},
request: {
copy: "复制请求",
@ -434,6 +439,9 @@ export default {
body: "请求内容",
body_kv: "键值对",
body_text: "文本",
timeout_config: "超时设置",
connect_timeout: "连接超时",
response_timeout: "响应超时",
assertions: {
label: "断言",
text: "文本",
@ -590,6 +598,16 @@ export default {
relate_test_not_find: '关联的测试不存在,请检查用例',
batch_handle: '批量处理 (选中{0}项)',
batch_update: '更新{0}个用例的属性',
select_catalog: '请选择用例目录',
updated_attr_value: '更新后属性值为',
batch_operate: '批量操作',
please_select_attr: '请选择属性',
please_select_attr_value: '请选择属性对应的值',
batch_edit_plan: '批量更改测试计划',
batch_edit_case: '批量编辑用例',
batch_move_case: '批量移动用例',
batch_delete_case: '批量删除用例',
batch_unlink: '批量取消用例关联',
import: {
import: "导入用例",
case_import: "导入测试用例",
@ -700,6 +718,20 @@ export default {
test_detail: "测试详情",
failure_case: "失败用例",
export_report: "导出报告"
},
issue: {
issue: "缺陷",
platform_tip: "在系统设置-组织-服务集成中集成缺陷管理平台可以自动提交缺陷到指定缺陷管理平台",
input_title: "请输入标题",
id: "缺陷ID",
title: "缺陷标题",
description: "缺陷描述",
status: "缺陷状态",
platform: "平台",
operate: "操作",
close: "关闭缺陷",
title_description_required: "标题和描述必填",
close_success: "关闭成功",
}
},
test_resource_pool: {

View File

@ -105,6 +105,7 @@ export default {
formatErr: '格式錯誤',
please_save: '請先保存',
id: 'ID',
millisecond: '毫秒',
reference_documentation: "參考文檔",
please_upload: '請上傳文件',
date: {
@ -211,6 +212,8 @@ export default {
owning_workspace: '所屬工作空間',
please_choose_workspace: '請選擇工作空間',
special_characters_are_not_supported: '格式錯誤(不支持特殊字符,且不能以\'-\'開頭結尾)',
tapd_id: 'TAPD項目ID',
jira_key: 'JIRA項目key',
},
member: {
create: '添加成員',
@ -396,7 +399,9 @@ export default {
headers: "請求頭",
kv_description: "所有請求可以使用自定義變數",
copy: "複製場景",
delete: "删除場景"
delete: "删除場景",
disable: "禁用",
enable: "啟用"
},
request: {
copy: "複製請求",
@ -433,6 +438,9 @@ export default {
body: "請求內容",
body_kv: "鍵值對",
body_text: "文字",
timeout_config: "超時設置",
connect_timeout: "連接超時",
response_timeout: "響應超時",
assertions: {
label: "斷言",
text: "文字",
@ -587,6 +595,16 @@ export default {
relate_test_not_find: '關聯的測試不存在,請檢查用例',
batch_handle: '批量處理 (選中{0}項)',
batch_update: '更新{0}個用例的屬性',
select_catalog: '請選擇用例目錄',
updated_attr_value: '更新後屬性值為',
batch_operate: '批量操作',
please_select_attr: '請選擇屬性',
please_select_attr_value: '請選擇屬性對應的值',
batch_edit_plan: '批量更改測試計劃',
batch_edit_case: '批量編輯用例',
batch_move_case: '批量移動用例',
batch_delete_case: '批量刪除用例',
batch_unlink: '批量取消用例關聯',
import: {
import: "導入用例",
case_import: "導入測試用例",
@ -697,6 +715,20 @@ export default {
test_detail: "測試詳情",
failure_case: "失敗用例",
export_report: "匯出報告"
},
issue: {
issue: "缺陷",
platform_tip: "在系統設置-組織-服務集成中集成缺陷管理平台可以自動提交缺陷到指定缺陷管理平台",
input_title: "請輸入標題",
id: "缺陷ID",
title: "缺陷標題",
description: "缺陷描述",
status: "缺陷狀態",
platform: "平台",
operate: "操作",
close: "關閉缺陷",
title_description_required: "標題和描述必填",
close_success: "關閉成功",
}
},
test_resource_pool: {