This commit is contained in:
liqiang-fit2cloud 2022-12-21 18:20:03 +08:00
commit 232a659656
22 changed files with 770 additions and 803 deletions

View File

@ -29,7 +29,7 @@ public interface ExtApiDefinitionExecResultMapper {
long countByTestCaseIDInProject(String projectId);
List<ExecutedCaseInfoResult> findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("version") String version, @Param("selectFunctionCase") boolean selectFunctionCase, @Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber);
List<ExecutedCaseInfoResult> findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("versionId") String version, @Param("selectFunctionCase") boolean selectFunctionCase, @Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber);
String selectExecResult(String resourceId);

View File

@ -383,7 +383,6 @@
</if>
AND create_time BETWEEN #{firstDayTimestamp} AND #{lastDayTimestamp}
</select>
<select id="countByProjectIdGroupByExecuteResult" resultType="io.metersphere.api.dto.datacount.ApiDataCountResult">
SELECT count(id) AS countNumber, result AS groupField
FROM scenario_execution_info

View File

@ -32,8 +32,7 @@ public class MockApiController {
public String postRequest(@PathVariable String projectSystemId, HttpServletRequest request, HttpServletResponse response) {
Project project = baseProjectService.findBySystemId(projectSystemId);
Map<String, String> requestHeaderMap = MockApiUtils.getHttpRequestHeader(request);
String returnStr = mockConfigService.checkReturnWithMockExpectByBodyParam("POST", requestHeaderMap, project, request, response);
return returnStr;
return mockConfigService.checkReturnWithMockExpectByBodyParam("POST", requestHeaderMap, project, request, response);
}
@GetMapping("/{projectSystemId}/**")
@ -41,8 +40,7 @@ public class MockApiController {
public String getRequest(@PathVariable String projectSystemId, HttpServletRequest request, HttpServletResponse response) {
Project project = baseProjectService.findBySystemId(projectSystemId);
Map<String, String> requestHeaderMap = MockApiUtils.getHttpRequestHeader(request);
String returnStr = mockConfigService.checkReturnWithMockExpectByUrlParam("GET", requestHeaderMap, project, request, response);
return returnStr;
return mockConfigService.checkReturnWithMockExpectByUrlParam("GET", requestHeaderMap, project, request, response);
}
@PutMapping("/{projectSystemId}/**")
@ -50,8 +48,7 @@ public class MockApiController {
public String putRequest(@PathVariable String projectSystemId, HttpServletRequest request, HttpServletResponse response) {
Project project = baseProjectService.findBySystemId(projectSystemId);
Map<String, String> requestHeaderMap = MockApiUtils.getHttpRequestHeader(request);
String returnStr = mockConfigService.checkReturnWithMockExpectByBodyParam("PUT", requestHeaderMap, project, request, response);
return returnStr;
return mockConfigService.checkReturnWithMockExpectByBodyParam("PUT", requestHeaderMap, project, request, response);
}
@PatchMapping("/{projectSystemId}/**")
@ -59,8 +56,7 @@ public class MockApiController {
public String patchRequest(@PathVariable String projectSystemId, HttpServletRequest request, HttpServletResponse response) {
Project project = baseProjectService.findBySystemId(projectSystemId);
Map<String, String> requestHeaderMap = MockApiUtils.getHttpRequestHeader(request);
String returnStr = mockConfigService.checkReturnWithMockExpectByBodyParam("PATCH", requestHeaderMap, project, request, response);
return returnStr;
return mockConfigService.checkReturnWithMockExpectByBodyParam("PATCH", requestHeaderMap, project, request, response);
}
@DeleteMapping("/{projectSystemId}/**")
@ -68,8 +64,7 @@ public class MockApiController {
public String deleteRequest(@PathVariable String projectSystemId, HttpServletRequest request, HttpServletResponse response) {
Project project = baseProjectService.findBySystemId(projectSystemId);
Map<String, String> requestHeaderMap = MockApiUtils.getHttpRequestHeader(request);
String returnStr = mockConfigService.checkReturnWithMockExpectByUrlParam("DELETE", requestHeaderMap, project, request, response);
return returnStr;
return mockConfigService.checkReturnWithMockExpectByUrlParam("DELETE", requestHeaderMap, project, request, response);
}
@RequestMapping(value = "/{projectSystemId}/**", method = RequestMethod.OPTIONS)
@ -77,8 +72,7 @@ public class MockApiController {
public String optionsRequest(@PathVariable String projectSystemId, HttpServletRequest request, HttpServletResponse response) {
Project project = baseProjectService.findBySystemId(projectSystemId);
Map<String, String> requestHeaderMap = MockApiUtils.getHttpRequestHeader(request);
String returnStr = mockConfigService.checkReturnWithMockExpectByUrlParam("OPTIONS", requestHeaderMap, project, request, response);
return returnStr;
return mockConfigService.checkReturnWithMockExpectByUrlParam("OPTIONS", requestHeaderMap, project, request, response);
}
@RequestMapping(value = "/{projectSystemId}/**", method = RequestMethod.HEAD)

View File

@ -127,11 +127,17 @@ public class MockConfigService {
}
}
/**
* 这个接口是生成用的
* 如果没数据就生成有数据就返回一套数据结构
* 所有入参都没有必填项
* 如果为了查询请写一个新的接口
*
* @param request
* @return
*/
public MockConfigResponse genMockConfig(MockConfigRequest request) {
MockConfigResponse returnRsp;
if (StringUtils.isEmpty(request.getId())) {
return new MockConfigResponse(null, new ArrayList<>());
}
MockConfigExample example = new MockConfigExample();
MockConfigExample.Criteria criteria = example.createCriteria();
if (request.getId() != null) {
@ -139,6 +145,8 @@ public class MockConfigService {
}
if (request.getApiId() != null) {
criteria.andApiIdEqualTo(request.getApiId());
} else if (StringUtils.isEmpty(request.getId())) {
return new MockConfigResponse(null, new ArrayList<>());
}
if (request.getProjectId() != null) {
criteria.andProjectIdEqualTo(request.getProjectId());

View File

@ -1095,6 +1095,12 @@ public class ApiDefinitionService {
}
apiDefinitionRequest.setVersionId(api.getVersionId());
checkNameExist(apiDefinitionRequest, false);
//同步修改所有版本的模块路径
if (StringUtils.isNotEmpty(request.getModuleId()) && StringUtils.isNotEmpty(request.getModulePath())) {
api.setModuleId(request.getModuleId());
api.setModulePath(request.getModulePath());
updateOtherVersionModule(api);
}
});
if (StringUtils.isNotEmpty(request.getFollow())) {
if (StringUtils.equals(request.getFollow(), "cancel")) {

View File

@ -1139,6 +1139,11 @@ public class ApiScenarioService {
scenarioRequest.setModulePath(request.getModulePath());
scenarioRequest.setVersionId(scenario.getVersionId());
checkNameExist(scenarioRequest, false);
if (StringUtils.isNotEmpty(request.getApiScenarioModuleId()) && StringUtils.isNotEmpty(request.getModulePath())) {
scenario.setApiScenarioModuleId(request.getApiScenarioModuleId());
scenario.setModulePath(request.getModulePath());
updateOtherVersionModule(scenario.getRefId(), scenario);
}
});
}
apiScenarioMapper.updateByExampleSelective(apiScenarioWithBLOBs, apiScenarioExample);

View File

@ -323,6 +323,7 @@ export default {
},
mockSetting() {
let mockParam = {};
mockParam.projectId = this.projectId;
if (this.currentApi.id) {
mockParam.apiId = this.currentApi.id;

View File

@ -3,7 +3,7 @@
<ms-container>
<ms-main-container style="padding: 0px">
<div class="api-home-layout">
<el-row class="api-home-toolbar">
<el-row v-if="showVersionSelector" class="api-home-toolbar">
<el-select
clearable
v-model="versionId"
@ -78,6 +78,7 @@ export default {
versionId: '',
projectId: getCurrentProjectID(),
versions: [],
showVersionSelector: false,
};
},
created() {
@ -85,6 +86,7 @@ export default {
this.$nextTick(() => {
this.refreshAllCard();
});
this.showVersionSelector = hasLicense();
},
watch: {
versionId() {

View File

@ -45,57 +45,11 @@
</el-input>
</div>
<!-- 接口测试配置 -->
<form-section :title="$t('commons.api')" :init-active=true>
<p>{{ $t('api_test.request.headers') }}</p>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: #783887"> {{
$t("commons.batch_add")
}}
</el-link>
</el-row>
<ms-api-key-value :items="condition.headers" :isShowEnable="true" :suggestions="headerSuggestions"/>
</form-section>
<!-- UI 配置 -->
<form-section :title="$t('commons.ui_test')" :init-active=false v-if="condition.type !== 'MODULE'">
<el-row :gutter="10" style="padding-top: 10px;">
<el-col :span="6">
<!-- 浏览器驱动 -->
<span style="margin-right: 10px;">{{ $t("ui.browser") }}</span>
<el-select
size="mini"
v-model="httpConfig.browser"
style="width: 100px"
>
<el-option
v-for="b in browsers"
:key="b.value"
:value="b.value"
:label="b.label"
></el-option>
</el-select>
</el-col>
<el-col :span="6">
<!-- 性能模式 -->
<el-checkbox
v-model="httpConfig.headlessEnabled"
>
<span> {{ $t("ui.performance_mode") }}</span>
</el-checkbox>
<ms-instructions-icon size="10" :content="$t('ui.per_tip')"/>
</el-col>
</el-row>
<!-- 当前版本实现免登录是基于 cookie 的但是现在由于安全性问题绝大多数网站都不支持 cookie登录所以先屏蔽了-->
<!-- <el-row :gutter="10">-->
<!-- <el-col :span="24">-->
<!-- <ms-ui-scenario-cookie-table :items="httpConfig.cookie" ref="cookieTable"/>-->
<!-- </el-col>-->
<!-- </el-row>-->
</form-section>
<p>{{ $t('api_test.request.headers') }}</p>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: #783887"> {{ $t("commons.batch_add") }}</el-link>
</el-row>
<ms-api-key-value :items="condition.headers" :isShowEnable="true" :suggestions="headerSuggestions"/>
<div style="margin-top: 20px">
<el-button v-if="!condition.id" type="primary" style="float: right" size="mini" @click="add">
{{ $t('commons.add') }}
@ -167,12 +121,10 @@ import {getUUID} from "../../utils";
import {KeyValue} from "../../model/EnvTestModel";
import Vue from "vue";
import BatchAddParameter from "./commons/BatchAddParameter";
import FormSection from "metersphere-frontend/src/components/form/FormSection";
import MsInstructionsIcon from 'metersphere-frontend/src/components/MsInstructionsIcon';
export default {
name: "MsEnvironmentHttpConfig",
components: {MsApiKeyValue, MsSelectTree, MsTableOperatorButton, BatchAddParameter, FormSection, MsInstructionsIcon},
components: {MsApiKeyValue, MsSelectTree, MsTableOperatorButton, BatchAddParameter},
props: {
httpConfig: new HttpConfig(),
projectId: String,
@ -213,21 +165,9 @@ export default {
socket: "",
domain: "",
port: 0,
headers: [new KeyValue()],
headlessEnabled: true,
browser: 'CHROME'
headers: [new KeyValue()]
},
beforeCondition: {},
browsers: [
{
label: this.$t("chrome"),
value: "CHROME",
},
{
label: this.$t("firefox"),
value: "FIREFOX",
},
],
beforeCondition: {}
};
},
watch: {
@ -349,7 +289,7 @@ export default {
list() {
if (this.projectId) {
this.result = getApiModuleByProjectIdAndProtocol(this.projectId, "HTTP").then((response) => {
if (response.data && response.data !== null) {
if (response.data && response.data !== null) {
this.moduleOptions = response.data;
}
});

View File

@ -18,18 +18,12 @@
:content="$t('commons.import')"
@click="importJSON"
/>
<el-dropdown @command="handleExportCommand" class="scenario-ext-btn" trigger="hover"
v-permission="['PROJECT_ENVIRONMENT:READ+EXPORT']">
<ms-table-button
style="margin-left: 10px"
icon="el-icon-box"
:content="$t('commons.export')"
/>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="exportApi">{{ $t('envrionment.export_variable_tip') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<ms-table-button
v-permission="['PROJECT_ENVIRONMENT:READ+EXPORT']"
icon="el-icon-box"
:content="$t('commons.export')"
@click="exportJSON"
/>
<el-link
style="margin-left: 10px"
@click="batchAdd"
@ -64,31 +58,6 @@
>
<ms-table-column prop="num" sortable label="ID" min-width="60">
</ms-table-column>
<ms-table-column
prop="scope"
sortable
:label="$t('commons.scope')"
:filters="scopeTypeFilters"
:filter-method="filterScope"
min-width="120">
<template slot-scope="scope">
<el-select
v-model="scope.row.scope"
:placeholder="$t('commons.please_select')"
size="mini"
@change="changeType(scope.row)"
>
<el-option
v-for="item in scopeTypeFilters"
:key="item.value"
:label="item.text"
:value="item.value"
/>
</el-select>
</template>
</ms-table-column>
<ms-table-column
prop="name"
:label="$t('api_test.variable_name')"
@ -115,9 +84,9 @@
<template slot-scope="scope">
<el-select
v-model="scope.row.type"
v-if="!scope.row.scope || scope.row.scope == 'api'"
:placeholder="$t('commons.please_select')"
size="mini"
@change="changeType(scope.row)"
>
<el-option
v-for="item in typeSelectOptions"
@ -126,20 +95,6 @@
:value="item.value"
/>
</el-select>
<el-select
v-else
v-model="scope.row.type"
:placeholder="$t('commons.please_select')"
size="mini"
>
<el-option
v-for="item in uiTypeSelectOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</ms-table-column>
@ -173,14 +128,14 @@
sortable
>
<template slot-scope="scope">
<el-input v-model="scope.row.description" size="mini"/>
<el-input v-model="scope.row.description" size="mini" />
</template>
</ms-table-column>
<ms-table-column :label="$t('commons.operating')" width="150">
<template v-slot:default="scope">
<span>
<el-switch v-model="scope.row.enable" size="mini"/>
<el-switch v-model="scope.row.enable" size="mini" />
<el-tooltip
effect="dark"
:content="$t('commons.remove')"
@ -216,7 +171,7 @@
</ms-table-column>
</ms-table>
</div>
<batch-add-parameter @batchSave="batchSave" ref="batchAdd"/>
<batch-add-parameter @batchSave="batchSave" ref="batchAdd" />
<api-variable-setting ref="apiVariableSetting"></api-variable-setting>
<variable-import
ref="variableImport"
@ -226,7 +181,7 @@
</template>
<script>
import {KeyValue} from "../../../model/EnvTestModel";
import { KeyValue } from "../../../model/EnvTestModel";
import MsApiVariableInput from "./ApiVariableInput";
import BatchAddParameter from "./BatchAddParameter";
import MsTableButton from "../../MsTableButton";
@ -234,9 +189,8 @@ import MsTable from "../../table/MsTable";
import MsTableColumn from "../../table/MsTableColumn";
import ApiVariableSetting from "./ApiVariableSetting";
import CsvFileUpload from "./variable/CsvFileUpload";
import {downloadFile, getUUID, operationConfirm} from "../../../utils";
import { downloadFile, getUUID, operationConfirm } from "../../../utils";
import VariableImport from "./variable/VariableImport";
import _ from "lodash";
export default {
name: "MsApiScenarioVariables",
@ -276,25 +230,15 @@ export default {
},
],
typeSelectOptions: [
{value: "CONSTANT", label: this.$t("api_test.automation.constant")},
{value: "LIST", label: this.$t("test_track.case.list")},
{value: "CSV", label: "CSV"},
{value: "COUNTER", label: this.$t("api_test.automation.counter")},
{value: "RANDOM", label: this.$t("api_test.automation.random")},
],
uiTypeSelectOptions: [
{value: "STRING", label: this.$t("api_test.automation.string")},
{value: "ARRAY", label: this.$t("api_test.automation.array")},
{value: "JSON", label: this.$t("api_test.automation.json")},
{value: "NUMBER", label: this.$t("api_test.automation.number")},
{ value: "CONSTANT", label: this.$t("api_test.automation.constant") },
{ value: "LIST", label: this.$t("test_track.case.list") },
{ value: "CSV", label: "CSV" },
{ value: "COUNTER", label: this.$t("api_test.automation.counter") },
{ value: "RANDOM", label: this.$t("api_test.automation.random") },
],
variables: {},
selectVariable: "",
editData: {},
scopeTypeFilters: [
{text: this.$t("commons.api"), value: "api"},
{text: this.$t("commons.ui_test"), value: "ui"},
]
};
},
watch: {
@ -335,15 +279,15 @@ export default {
if (repeatKey !== "") {
this.$warning(
this.$t("api_test.environment.common_config") +
"【" +
repeatKey +
"】" +
this.$t("load_test.param_is_duplicate")
"【" +
repeatKey +
"】" +
this.$t("load_test.param_is_duplicate")
);
}
if (isNeedCreate) {
this.variables.push(
new KeyValue({enable: true, id: getUUID(), type: "CONSTANT", scope: "api"})
new KeyValue({ enable: true, id: getUUID(), type: "CONSTANT" })
);
}
this.$emit("change", this.variables);
@ -360,10 +304,6 @@ export default {
data.files = [];
data.quotedData = "false";
}
if (!data.scope || data.scope == "ui") {
data.type = 'STRING';
}
},
valueText(data) {
switch (data.type) {
@ -380,11 +320,11 @@ export default {
},
querySearch(queryString, cb) {
let restaurants = [
{value: "UTF-8"},
{value: "UTF-16"},
{value: "GB2312"},
{value: "ISO-8859-15"},
{value: "US-ASCll"},
{ value: "UTF-8" },
{ value: "UTF-16" },
{ value: "GB2312" },
{ value: "ISO-8859-15" },
{ value: "US-ASCll" },
];
let results = queryString
? restaurants.filter(this.createFilter(queryString))
@ -406,9 +346,6 @@ export default {
this.$set(item, "description", item.remark);
item.remark = undefined;
}
if (!item.scope) {
this.$set(item, "scope", "api");
}
index++;
});
},
@ -432,7 +369,7 @@ export default {
}
);
},
filter(scope) {
filter() {
let datas = [];
this.variables.forEach((item) => {
if (this.selectVariable && this.selectVariable != "" && item.name) {
@ -452,12 +389,6 @@ export default {
});
this.variables = datas;
},
filterScope(value, row) {
if (value == "ui") {
return row.scope == "ui";
}
return !row.scope || row.scope == "api";
},
openSetting(data) {
this.$refs.apiVariableSetting.open(data);
},
@ -518,15 +449,8 @@ export default {
this.sortParameters();
},
exportJSON() {
let apiVariable = [];
this.$refs.variableTable.selectRows.forEach((r) => {
if (!r.scope || r.scope != "ui") {
apiVariable.push(r);
}
});
if (apiVariable.length < 1) {
this.$warning(this.$t("api_test.environment.select_api_variable"));
if (this.$refs.variableTable.selectIds.length < 1) {
this.$warning(this.$t("api_test.environment.select_variable"));
return;
}
let variablesJson = [];
@ -536,7 +460,7 @@ export default {
if (row.type === "CSV") {
messages = this.$t("variables.csv_download");
}
if (row.name && (!row.scope || row.scope == "api")) {
if (row.name) {
variablesJson.push(row);
}
});
@ -569,21 +493,10 @@ export default {
}
});
},
handleExportCommand(command){
this.exportJSON();
}
},
created() {
if (this.items.length === 0) {
this.items.push(new KeyValue({enable: true, scope: "api"}));
} else {
// api
_.forEach(this.items, item => {
if (!item.scope) {
this.$set(item, "scope", "api");
}
})
this.variables = this.items;
this.items.push(new KeyValue({ enable: true }));
}
},
};

View File

@ -136,8 +136,8 @@ import MsTableOperator from "metersphere-frontend/src/components/MsTableOperator
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import ApiEnvironmentConfig from "metersphere-frontend/src/components/environment/ApiEnvironmentConfig";
import {Environment, parseEnvironment, HttpConfig} from "metersphere-frontend/src/model/EnvironmentModel";
import EnvironmentEdit from "./components/EnvironmentEdit";
import {Environment, parseEnvironment} from "metersphere-frontend/src/model/EnvironmentModel";
import EnvironmentEdit from "metersphere-frontend/src/components/environment/EnvironmentEdit";
import MsAsideItem from "metersphere-frontend/src/components/MsAsideItem";
import MsAsideContainer from "metersphere-frontend/src/components/MsAsideContainer";
import ProjectSwitch from "metersphere-frontend/src/components/head/ProjectSwitch";
@ -174,7 +174,7 @@ export default {
projectList: [],
condition: {}, //
environments: [],
currentEnvironment: new Environment({httpConfig: new HttpConfig()}),
currentEnvironment: new Environment(),
result: {},
loading: false,
dialogVisible: false,
@ -288,7 +288,7 @@ export default {
createEnv() {
this.dialogTitle = this.$t('api_test.environment.create');
this.dialogVisible = true;
this.currentEnvironment = new Environment({httpConfig: new HttpConfig()});
this.currentEnvironment = new Environment();
this.currentEnvironment.projectId = this.currentProjectId;
this.currentEnvironment.currentProjectId = this.currentProjectId;
this.ifCreate = true;

View File

@ -155,7 +155,7 @@ import {REQUEST_HEADERS} from "metersphere-frontend/src/utils/constants";
import {CommonConfig, Environment} from "metersphere-frontend/src/model/EnvironmentModel";
import MsApiHostTable from "metersphere-frontend/src/components/environment/commons/ApiHostTable";
import MsDatabaseConfig from "metersphere-frontend/src/components/environment/database/DatabaseConfig";
import MsEnvironmentHttpConfig from "./EnvironmentHttpConfig";
import MsEnvironmentHttpConfig from "metersphere-frontend/src/components/environment/EnvironmentHttpConfig";
import MsEnvironmentCommonConfig from "metersphere-frontend/src/components/environment/EnvironmentCommonConfig";
import MsEnvironmentSSLConfig from "metersphere-frontend/src/components/environment/EnvironmentSSLConfig";
import MsApiAuthConfig from "metersphere-frontend/src/components/environment/auth/ApiAuthConfig";

View File

@ -65,6 +65,8 @@ import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
@ -1548,10 +1550,6 @@ public class TestPlanService {
envMap = planTestPlanApiCaseService.getApiCaseEnv(planId);
Map<String, List<String>> scenarioEnv = planTestPlanScenarioCaseService.getApiScenarioEnv(planId);
if (DiscoveryUtil.hasService(MicroServiceName.UI_TEST)) {
scenarioEnv = mergeUiScenarioEnv(planId, scenarioEnv);
}
Set<String> projectIds = scenarioEnv.keySet();
for (String projectId : projectIds) {
if (envMap.containsKey(projectId)) {
@ -1574,32 +1572,6 @@ public class TestPlanService {
return envMap;
}
/**
* 合并ui场景的环境信息
* @param planId
* @param scenarioEnv
* @return
*/
private Map<String, List<String>> mergeUiScenarioEnv(String planId, Map<String, List<String>> scenarioEnv) {
Map<String, List<String>> uiScenarioEnv = planTestPlanUiScenarioCaseService.getUiScenarioEnv(planId);
if (MapUtils.isEmpty(scenarioEnv)) {
return uiScenarioEnv;
}
if (MapUtils.isNotEmpty(uiScenarioEnv)) {
uiScenarioEnv.entrySet().forEach(entry -> {
if (scenarioEnv.containsKey(entry.getKey())) {
List<String> environmentIds = scenarioEnv.get(entry.getKey());
entry.getValue().forEach(eId -> {
if (!environmentIds.contains(eId)) {
environmentIds.add(eId);
}
});
}
});
}
return scenarioEnv;
}
public String runPlan(TestPlanRunRequest testplanRunRequest) {
//检查测试计划下有没有可以执行的用例
if (!haveExecCase(testplanRunRequest.getTestPlanId(), false)) {

View File

@ -1857,6 +1857,22 @@ public class TestCaseService {
BeanUtils.copyBean(batchEdit, request);
batchEdit.setUpdateTime(System.currentTimeMillis());
bathUpdateByCondition(request, batchEdit);
//批量修改选中数据其他版本的模块路径
if (request != null && (request.getIds() != null || !request.getIds().isEmpty())) {
request.getIds().forEach(testCaseId -> {
TestCaseWithBLOBs testCaseWithBLOBs = testCaseMapper.selectByPrimaryKey(testCaseId);
if (testCaseWithBLOBs == null) {
return;
}
if (StringUtils.isNotEmpty(request.getNodeId()) && StringUtils.isNotEmpty(request.getNodePath())) {
testCaseWithBLOBs.setNodeId(request.getNodeId());
testCaseWithBLOBs.setNodePath(request.getNodePath());
EditTestCaseRequest editTestCaseRequest = new EditTestCaseRequest();
BeanUtils.copyBean(editTestCaseRequest, testCaseWithBLOBs);
updateOtherVersionModule(editTestCaseRequest);
}
});
}
}
}

View File

@ -241,7 +241,12 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
if (endpoint.endsWith("/")) {
endpoint = endpoint.substring(0, endpoint.length() - 1);
}
path = " <img src=\"" + endpoint + path + "\"/>";
String format = " <img src=\"%s\"/>";
if (path.trim().startsWith("http")) {
path = String.format(format, path);
} else {
path = String.format(format, endpoint + path);
}
result = matcher.replaceFirst(path);
matcher = pattern.matcher(result);
}

View File

@ -9,7 +9,8 @@
:plan-status="planStatus"
@refresh="search"
@relevanceCase="$emit('relevanceCase')"
v-if="isPlanModel"/>
v-if="isPlanModel"
/>
</template>
<ms-table
v-loading="loading"
@ -29,40 +30,54 @@
:enable-order-drag="enableOrderDrag"
row-key="id"
@filter="search"
ref="table">
<span v-for="(item) in fields" :key="item.key">
<ms-table-column :field="item"
:fields-width="fieldsWidth"
sortable
label="ID"
prop="num"
min-width="80">
<template v-slot:default="scope">
<span style="cursor:pointer"
v-if="!hasPermission('PROJECT_API_DEFINITION:READ+EDIT_CASE')"> {{ scope.row.num }} </span>
<el-link @click="openApiById(scope.row)" v-else>
<span>
{{ scope.row.num }}
</span>
</el-link>
ref="table"
>
<span v-for="item in fields" :key="item.key">
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
sortable
label="ID"
prop="num"
min-width="80"
>
<template v-slot:default="scope">
<span
style="cursor: pointer"
v-if="!hasPermission('PROJECT_API_DEFINITION:READ+EDIT_CASE')"
>
{{ scope.row.num }}
</span>
<el-link @click="openApiById(scope.row)" v-else>
<span>
{{ scope.row.num }}
</span>
</el-link>
</template>
</ms-table-column>
<ms-table-column :field="item" :fields-width="fieldsWidth" prop="name" sortable min-width="120"
:label="$t('test_track.case.name')"/>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="name"
sortable
min-width="120"
:label="$t('test_track.case.name')"
/>
<ms-table-column
v-if="versionEnable"
prop="versionId"
:field="item"
:filters="versionFilters"
:fields-width="fieldsWidth"
:label="$t('commons.version')"
min-width="120px">
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</ms-table-column>
<ms-table-column
v-if="versionEnable"
prop="versionId"
:field="item"
:filters="versionFilters"
:fields-width="fieldsWidth"
:label="$t('commons.version')"
min-width="120px"
>
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</ms-table-column>
<ms-table-column
:field="item"
@ -71,9 +86,10 @@
:filters="priorityFilters"
sortable
:label="$t('test_track.case.priority')"
min-width="120px">
min-width="120px"
>
<template v-slot:default="scope">
<priority-table-item :value="scope.row.priority"/>
<priority-table-item :value="scope.row.priority" />
</template>
</ms-table-column>
@ -82,7 +98,8 @@
:fields-width="fieldsWidth"
min-width="100"
prop="path"
:label="$t('api_test.definition.api_path')"/>
:label="$t('api_test.definition.api_path')"
/>
<ms-table-column
:field="item"
@ -91,7 +108,8 @@
sortable
min-width="100"
:filters="userFilters"
:label="$t('commons.create_user')">
:label="$t('commons.create_user')"
>
<template v-slot:default="scope">
{{ scope.row.creatorName }}
</template>
@ -103,9 +121,10 @@
prop="environmentName"
min-width="120"
show-overflow-tooltip
:label="$t('commons.environment')">
:label="$t('commons.environment')"
>
<template v-slot:default="scope">
{{ scope.row.environmentName || '-' }}
{{ scope.row.environmentName || "-" }}
</template>
</ms-table-column>
@ -114,39 +133,58 @@
prop="userId"
:fields-width="fieldsWidth"
:label="$t('custom_field.case_maintainer')"
min-width="120">
<template v-slot:default="scope">
min-width="120"
>
<template v-slot:default="scope">
{{ scope.row.principalName }}
</template>
</ms-table-column>
<ms-update-time-column :field="item" :fields-width="fieldsWidth"/>
<ms-create-time-column :field="item" :fields-width="fieldsWidth"/>
<ms-update-time-column :field="item" :fields-width="fieldsWidth" />
<ms-create-time-column :field="item" :fields-width="fieldsWidth" />
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="tags"
min-width="100"
:label="$t('commons.tag')">
:label="$t('commons.tag')"
>
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" style="margin-left: 0px; margin-right: 2px"/>
<ms-tag
v-for="(itemName, index) in scope.row.tags"
:key="index"
type="success"
effect="plain"
:content="itemName"
style="margin-left: 0px; margin-right: 2px"
/>
</template>
</ms-table-column>
<ms-table-column :field="item"
prop="execResult"
:filters="execResultFilters"
:fields-width="fieldsWidth"
:label="$t('test_track.plan.execute_result')" min-width="150" align="center">
<ms-table-column
:field="item"
prop="execResult"
:filters="execResultFilters"
:fields-width="fieldsWidth"
:label="$t('test_track.plan.execute_result')"
min-width="150"
align="center"
>
<template v-slot:default="scope">
<div v-loading="rowLoading === scope.row.id">
<el-link @click="getReportResult(scope.row)"
:disabled="!scope.row.execResult || scope.row.execResult==='PENDING'">
<ms-test-plan-api-status :status="scope.row.execResult"/>
</el-link>
<div v-if="scope.row.id" style="color: #999999;font-size: 12px">
<el-link
@click="getReportResult(scope.row)"
:disabled="
!scope.row.execResult || scope.row.execResult === 'PENDING'
"
>
<ms-test-plan-api-status :status="scope.row.execResult" />
</el-link>
<div
v-if="scope.row.id"
style="color: #999999; font-size: 12px"
>
<span> {{ scope.row.updateTime | datetimeFormat }}</span>
{{ scope.row.updateUser }}
</div>
@ -156,45 +194,66 @@
</span>
</ms-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<ms-table-pagination
:change="initTable"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
/>
<test-plan-api-case-result ref="apiCaseResult"/>
<test-plan-api-case-result ref="apiCaseResult" />
<!-- 批量编辑 -->
<batch-edit :dialog-title="$t('test_track.case.batch_edit_case')" :type-arr="typeArr" :value-arr="valueArr"
:select-row="$refs.table ? $refs.table.selectRows : new Set()" ref="batchEdit"
@batchEdit="batchEdit"/>
<batch-edit
:dialog-title="$t('test_track.case.batch_edit_case')"
:type-arr="typeArr"
:value-arr="valueArr"
:select-row="$refs.table ? $refs.table.selectRows : new Set()"
ref="batchEdit"
@batchEdit="batchEdit"
/>
<ms-test-plan-run-mode-with-env @handleRunBatch="handleRunBatch" ref="runMode" :plan-case-ids="testPlanCaseIds" :type="'apiCase'"
@close="search" />
<ms-test-plan-run-mode-with-env
@handleRunBatch="handleRunBatch"
ref="runMode"
:plan-case-ids="testPlanCaseIds"
:type="'apiCase'"
@close="search"
/>
</el-card>
<ms-task-center ref="taskCenter" :show-menu="false"/>
<ms-task-center ref="taskCenter" :show-menu="false" />
</div>
</template>
<script>
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import MsTag from "metersphere-frontend/src/components/MsTag";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsBottomContainer from "metersphere-frontend/src/components/MsBottomContainer";
import BatchEdit from "@/business/case/components/BatchEdit";
import {API_METHOD_COLOUR, CASE_PRIORITY, RESULT_MAP} from "metersphere-frontend/src/model/JsonData";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {hasLicense, hasPermission} from "metersphere-frontend/src/utils/permission";
import {getUUID, strMapToObj} from "metersphere-frontend/src/utils";
import {
API_METHOD_COLOUR,
CASE_PRIORITY,
RESULT_MAP,
} from "metersphere-frontend/src/model/JsonData";
import {
getCurrentProjectID,
getCurrentWorkspaceId,
} from "metersphere-frontend/src/utils/token";
import {
hasLicense,
hasPermission,
} from "metersphere-frontend/src/utils/permission";
import { getUUID, strMapToObj } from "metersphere-frontend/src/utils";
import PriorityTableItem from "../../../../common/tableItems/planview/PriorityTableItem";
import TestPlanCaseListHeader from "./TestPlanCaseListHeader";
import TestPlanApiCaseResult from "./TestPlanApiCaseResult";
import {TEST_PLAN_API_CASE} from "metersphere-frontend/src/utils/constants";
import { TEST_PLAN_API_CASE } from "metersphere-frontend/src/utils/constants";
import {
buildBatchParam,
deepClone,
getCustomTableHeader,
getCustomTableWidth
getCustomTableWidth,
} from "metersphere-frontend/src/utils/tableUtils";
import HeaderCustom from "metersphere-frontend/src/components/head/HeaderCustom";
import HeaderLabelOperate from "metersphere-frontend/src/components/head/HeaderLabelOperate";
@ -203,20 +262,27 @@ import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import MsUpdateTimeColumn from "metersphere-frontend/src/components/table/MsUpdateTimeColumn";
import MsCreateTimeColumn from "metersphere-frontend/src/components/table/MsCreateTimeColumn";
import {editTestPlanApiCaseOrder, run, testPlanAutoCheck} from "@/api/remote/plan/test-plan";
import {getProjectMemberUserFilter} from "@/api/user";
import {apiTestCaseGet, apiTestCaseReduction} from "@/api/remote/api/api-case";
import {
editTestPlanApiCaseOrder,
run,
testPlanAutoCheck,
} from "@/api/remote/plan/test-plan";
import { getProjectMemberUserFilter } from "@/api/user";
import {
apiTestCaseGet,
apiTestCaseReduction,
} from "@/api/remote/api/api-case";
import {
testPlanApiCaseBatchDelete,
testPlanApiCaseBatchUpdateEnv,
testPlanApiCaseDelete,
testPlanApiCaseList,
testPlanApiCaseRun,
testPlanApiCaseSelectAllTableRows
testPlanApiCaseSelectAllTableRows,
} from "@/api/remote/plan/test-plan-api-case";
import MsTestPlanApiStatus from "@/business/plan/view/comonents/api/TestPlanApiStatus";
import {getProjectVersions} from "@/business/utils/sdk-utils";
import {TEST_PLAN_API_CASE_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import { getProjectVersions } from "@/business/utils/sdk-utils";
import { TEST_PLAN_API_CASE_CONFIGS } from "metersphere-frontend/src/components/search/search-components";
import MsTestPlanRunModeWithEnv from "@/business/plan/common/TestPlanRunModeWithEnv";
export default {
@ -247,67 +313,72 @@ export default {
return {
type: TEST_PLAN_API_CASE,
tableHeaderKey: "TEST_PLAN_API_CASE",
fields: getCustomTableHeader('TEST_PLAN_API_CASE'),
fieldsWidth: getCustomTableWidth('TEST_PLAN_API_CASE'),
fields: getCustomTableHeader("TEST_PLAN_API_CASE"),
fieldsWidth: getCustomTableWidth("TEST_PLAN_API_CASE"),
tableLabel: [],
condition: {
components: TEST_PLAN_API_CASE_CONFIGS
components: TEST_PLAN_API_CASE_CONFIGS,
},
selectCase: {},
loading: false,
moduleId: "",
status: 'default',
status: "default",
deletePath: "/test/case/delete",
enableOrderDrag: true,
operators: [
{
tip: this.$t('api_test.run'), icon: "el-icon-video-play",
tip: this.$t("api_test.run"),
icon: "el-icon-video-play",
exec: this.singleRun,
class: this.planStatus === 'Archived' ? 'disable-run' : 'run-button',
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+RUN']
class: this.planStatus === "Archived" ? "disable-run" : "run-button",
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+RUN"],
},
{
tip: this.$t('test_track.plan_view.cancel_relevance'), icon: "el-icon-unlock",
tip: this.$t("test_track.plan_view.cancel_relevance"),
icon: "el-icon-unlock",
exec: this.handleDelete,
type: 'danger',
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+RELEVANCE_OR_CANCEL']
}
type: "danger",
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+RELEVANCE_OR_CANCEL"],
},
],
buttons: [
{
name: this.$t('test_track.case.batch_unlink'),
name: this.$t("test_track.case.batch_unlink"),
handleClick: this.handleDeleteBatch,
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+CASE_BATCH_DELETE']
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+CASE_BATCH_DELETE"],
},
{
name: this.$t('api_test.automation.batch_execute'),
name: this.$t("api_test.automation.batch_execute"),
handleClick: this.handleBatchExecute,
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+CASE_BATCH_RUN']
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+CASE_BATCH_RUN"],
},
{
name: this.$t('test_track.case.batch_edit_case'),
name: this.$t("test_track.case.batch_edit_case"),
handleClick: this.handleBatchEdit,
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+CASE_BATCH_EDIT']
}
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+CASE_BATCH_EDIT"],
},
],
typeArr: [
{id: 'projectEnv', name: this.$t('api_test.definition.request.run_env')},
{
id: "projectEnv",
name: this.$t("api_test.definition.request.run_env"),
},
],
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
{ text: "P0", value: "P0" },
{ text: "P1", value: "P1" },
{ text: "P2", value: "P2" },
{ text: "P3", value: "P3" },
],
valueArr: {
priority: CASE_PRIORITY,
userId: [],
projectEnv: []
projectEnv: [],
},
methodColorMap: new Map(API_METHOD_COLOUR),
tableData: [],
@ -315,7 +386,7 @@ export default {
pageSize: 10,
total: 0,
selectDataCounts: 0,
screenHeight: 'calc(100vh - 250px)',//
screenHeight: "calc(100vh - 250px)", //
currentCaseProjectId: "",
runData: [],
testPlanCaseIds: [],
@ -327,11 +398,11 @@ export default {
projectList: [],
versionFilters: [],
execResultFilters: [
{text: 'Pending', value: 'PENDING'},
{text: 'Success', value: 'SUCCESS'},
{text: 'Error', value: 'ERROR'},
{text: "FakeError", value: 'FAKE_ERROR'},
]
{ text: "Pending", value: "PENDING" },
{ text: "Success", value: "SUCCESS" },
{ text: "Error", value: "ERROR" },
{ text: "FakeError", value: "FAKE_ERROR" },
],
};
},
props: {
@ -347,7 +418,7 @@ export default {
},
isReadOnly: {
type: Boolean,
default: false
default: false,
},
isCaseRelevance: {
type: Boolean,
@ -356,8 +427,8 @@ export default {
model: {
type: String,
default() {
'api';
}
"api";
},
},
planId: String,
planStatus: String,
@ -375,7 +446,7 @@ export default {
this.$EventBus.$off("API_TEST_END", this.handleTestEnd);
},
activated() {
this.status = 'default';
this.status = "default";
},
watch: {
selectNodeIds() {
@ -387,20 +458,20 @@ export default {
},
planId() {
this.initTable();
}
},
},
computed: {
//
isRelevanceModel() {
return this.model === 'relevance';
return this.model === "relevance";
},
//
isPlanModel() {
return this.model === 'plan';
return this.model === "plan";
},
//
isApiModel() {
return this.model === 'api';
return this.model === "api";
},
editTestPlanApiCaseOrder() {
return editTestPlanApiCaseOrder;
@ -418,7 +489,7 @@ export default {
});
},
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
this.$emit("isApiListEnableChange", data);
},
initTable() {
if (this.$refs.table) {
@ -431,30 +502,35 @@ export default {
this.condition.protocol = this.currentProtocol;
}
this.enableOrderDrag = (this.condition.orders && this.condition.orders.length) > 0 ? false : true;
this.enableOrderDrag =
(this.condition.orders && this.condition.orders.length) > 0
? false
: true;
if (this.clickType) {
if (this.status == 'default') {
if (this.status == "default") {
this.condition.status = this.clickType;
} else {
this.condition.status = null;
}
this.status = 'all';
this.status = "all";
}
if (this.planId) {
this.condition.planId = this.planId;
this.loading = true;
testPlanApiCaseList({pageNum: this.currentPage, pageSize: this.pageSize}, this.condition)
.then(response => {
this.loading = false;
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
this.tableData.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
testPlanApiCaseList(
{ pageNum: this.currentPage, pageSize: this.pageSize },
this.condition
).then((response) => {
this.loading = false;
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
this.tableData.forEach((item) => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
});
}
},
search() {
@ -466,34 +542,36 @@ export default {
},
reductionApi(row) {
let ids = [row.id];
apiTestCaseReduction(ids)
.then(() => {
this.$success(this.$t('commons.save_success'));
this.search();
});
apiTestCaseReduction(ids).then(() => {
this.$success(this.$t("commons.save_success"));
this.search();
});
},
handleDeleteBatch() {
this.$alert(this.$t('test_track.plan_view.confirm_cancel_relevance') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let param = buildBatchParam(this);
param.ids = this.$refs.table.selectIds;
if (this.planId) {
param.planId = this.planId;
testPlanApiCaseBatchDelete(param)
.then(() => {
this.$alert(
this.$t("test_track.plan_view.confirm_cancel_relevance") + "",
"",
{
confirmButtonText: this.$t("commons.confirm"),
callback: (action) => {
if (action === "confirm") {
let param = buildBatchParam(this);
param.ids = this.$refs.table.selectIds;
if (this.planId) {
param.planId = this.planId;
testPlanApiCaseBatchDelete(param).then(() => {
if (this.$refs.table) {
this.$refs.table.clear();
}
this.initTable();
this.$emit('refresh');
this.$success(this.$t('test_track.cancel_relevance_success'));
this.$emit("refresh");
this.$success(this.$t("test_track.cancel_relevance_success"));
});
}
}
}
},
}
});
);
},
getResult(data) {
if (RESULT_MAP.get(data)) {
@ -503,8 +581,8 @@ export default {
}
},
runRefresh() {
this.rowLoading = '';
this.$success(this.$t('schedule.event_success'));
this.rowLoading = "";
this.$success(this.$t("schedule.event_success"));
this.autoCheckStatus();
this.initTable();
},
@ -524,7 +602,9 @@ export default {
}
},
handleBatchEdit() {
this.$refs.batchEdit.open(this.condition.selectAll ? this.total : this.$refs.table.selectRows.size);
this.$refs.batchEdit.open(
this.condition.selectAll ? this.total : this.$refs.table.selectRows.size
);
this.$refs.batchEdit.setSelectRows(this.$refs.table.selectRows);
},
getData() {
@ -535,46 +615,45 @@ export default {
if (this.condition != null && this.condition.selectAll) {
let selectAllRowParams = buildBatchParam(this);
selectAllRowParams.ids = this.$refs.table.selectIds;
testPlanApiCaseSelectAllTableRows(selectAllRowParams)
.then(response => {
testPlanApiCaseSelectAllTableRows(selectAllRowParams).then(
(response) => {
let dataRows = response.data;
//
this.orderBySelectRows(dataRows);
dataRows.forEach(row => {
apiTestCaseGet(row.caseId)
.then((response) => {
let apiCase = response.data;
let request = JSON.parse(apiCase.request);
request.name = row.id;
request.id = row.id;
request.useEnvironment = row.environmentId;
this.runData.unshift(request);
this.testPlanCaseIds.unshift(row.id);
if (dataRows.length === index) {
resolve();
}
index++;
});
dataRows.forEach((row) => {
apiTestCaseGet(row.caseId).then((response) => {
let apiCase = response.data;
let request = JSON.parse(apiCase.request);
request.name = row.id;
request.id = row.id;
request.useEnvironment = row.environmentId;
this.runData.unshift(request);
this.testPlanCaseIds.unshift(row.id);
if (dataRows.length === index) {
resolve();
}
index++;
});
});
});
}
);
} else {
//
let dataRows = this.orderBySelectRows(this.$refs.table.selectRows);
dataRows.forEach(row => {
apiTestCaseGet(row.caseId)
.then((response) => {
let apiCase = response.data;
let request = JSON.parse(apiCase.request);
request.name = row.id;
request.id = row.id;
request.useEnvironment = row.environmentId;
this.runData.unshift(request);
this.testPlanCaseIds.unshift(row.id);
if (dataRows.length === index) {
resolve();
}
index++;
});
dataRows.forEach((row) => {
apiTestCaseGet(row.caseId).then((response) => {
let apiCase = response.data;
let request = JSON.parse(apiCase.request);
request.name = row.id;
request.id = row.id;
request.useEnvironment = row.environmentId;
this.runData.unshift(request);
this.testPlanCaseIds.unshift(row.id);
if (dataRows.length === index) {
resolve();
}
index++;
});
});
}
});
@ -582,32 +661,32 @@ export default {
batchEdit(form) {
let param = {};
//
if (form.type === 'projectEnv') {
if (form.type === "projectEnv") {
let selectAllRowParams = buildBatchParam(this);
selectAllRowParams.ids = this.$refs.table.selectIds;
testPlanApiCaseSelectAllTableRows(selectAllRowParams)
.then(response => {
testPlanApiCaseSelectAllTableRows(selectAllRowParams).then(
(response) => {
let dataRows = response.data;
let map = new Map();
param.projectEnvMap = strMapToObj(form.projectEnvMap);
param.environmentType = form.environmentType;
param.environmentGroupId = form.envGroupId;
dataRows.forEach(row => {
dataRows.forEach((row) => {
map[row.id] = row.projectId;
});
param.selectRows = map;
testPlanApiCaseBatchUpdateEnv(param)
.then(() => {
this.$success(this.$t('commons.save_success'));
this.initTable();
});
});
testPlanApiCaseBatchUpdateEnv(param).then(() => {
this.$success(this.$t("commons.save_success"));
this.initTable();
});
}
);
} else {
//
}
},
orderBySelectRows(rows) {
let selectIds = Array.from(rows).map(row => row.id);
let selectIds = Array.from(rows).map((row) => row.id);
let array = [];
for (let i in this.tableData) {
if (selectIds.indexOf(this.tableData[i].id) !== -1) {
@ -619,21 +698,26 @@ export default {
handleBatchExecute() {
this.getData().then(() => {
if (this.runData && this.runData.length > 0) {
this.$refs.runMode.open('API');
this.$refs.runMode.open("API");
}
});
},
handleRunBatch(config) {
let obj = {planIds: this.testPlanCaseIds, config: config, triggerMode: "BATCH",projectId: getCurrentProjectID()};
let obj = {
planIds: this.testPlanCaseIds,
config: config,
triggerMode: "BATCH",
projectId: getCurrentProjectID(),
};
obj.config.envMap = strMapToObj(config.envMap);
testPlanApiCaseRun(obj)
.then(() => {
this.$message(this.$t('commons.run_message'));
this.$refs.taskCenter.open();
this.search();
});
testPlanApiCaseRun(obj).then(() => {
this.$message(this.$t("commons.run_message"));
this.$refs.taskCenter.open();
this.search();
});
},
autoCheckStatus() { //
autoCheckStatus() {
//
if (!this.planId) {
return;
}
@ -641,12 +725,11 @@ export default {
},
handleDelete(apiCase) {
if (this.planId) {
testPlanApiCaseDelete(apiCase.id)
.then(() => {
this.$success(this.$t('test_track.cancel_relevance_success'));
this.$emit('refresh');
this.initTable();
});
testPlanApiCaseDelete(apiCase.id).then(() => {
this.$success(this.$t("test_track.cancel_relevance_success"));
this.$emit("refresh");
this.initTable();
});
}
return;
},
@ -662,18 +745,28 @@ export default {
},
getVersionOptions() {
if (hasLicense()) {
getProjectVersions(getCurrentProjectID())
.then(response => {
this.versionOptions = response.data;
this.versionFilters = response.data.map(u => {
return {text: u.name, value: u.id};
});
getProjectVersions(getCurrentProjectID()).then((response) => {
this.versionOptions = response.data;
this.versionFilters = response.data.map((u) => {
return { text: u.name, value: u.id };
});
});
}
},
openApiById(item) {
let definitionData = this.$router.resolve('/api/definition/' + getUUID() + "/apiTestCase/single:" + item.caseId + "/" + getCurrentProjectID() + "/" + item.protocol + "/" + getCurrentWorkspaceId());
window.open(definitionData.href, '_blank');
let definitionData = this.$router.resolve(
"/api/definition/default/" +
getUUID() +
"/apiTestCase/single:" +
item.caseId +
"/" +
getCurrentProjectID() +
"/" +
item.protocol +
"/" +
getCurrentWorkspaceId()
);
window.open(definitionData.href, "_blank");
},
},
};
@ -687,7 +780,7 @@ export default {
.request-method {
padding: 0 5px;
color: #1E90FF;
color: #1e90ff;
}
.api-el-tag {
@ -700,5 +793,4 @@ export default {
/*margin-bottom: 20px;*/
margin-right: 20px;
}
</style>

View File

@ -7,7 +7,8 @@
:projectId="projectId"
:plan-status="planStatus"
@refresh="filterSearch"
@relevanceCase="$emit('relevanceCase', 'scenario')"/>
@relevanceCase="$emit('relevanceCase', 'scenario')"
/>
</template>
<ms-table
@ -27,31 +28,40 @@
:row-order-group-id="planId"
@order="search"
@filter="filterSearch"
ref="table">
<span v-for="(item) in fields" :key="item.key">
ref="table"
>
<span v-for="item in fields" :key="item.key">
<ms-table-column
v-if="item.id == 'num'"
:fields-width="fieldsWidth"
sortable
prop="customNum"
min-width="80px"
label="ID">
<template v-slot:default="scope">
<span style="cursor:pointer"
v-if="!hasPermission('PROJECT_API_SCENARIO:READ+EDIT')"> {{ scope.row.num }} </span>
<el-link @click="openById(scope.row)" v-else>
<span>
{{ scope.row.customNum }}
</span>
</el-link>
label="ID"
>
<template v-slot:default="scope">
<span
style="cursor: pointer"
v-if="!hasPermission('PROJECT_API_SCENARIO:READ+EDIT')"
>
{{ scope.row.num }}
</span>
<el-link @click="openById(scope.row)" v-else>
<span>
{{ scope.row.customNum }}
</span>
</el-link>
</template>
</ms-table-column>
<ms-table-column :field="item"
:fields-width="fieldsWidth"
prop="name"
:label="$t('api_test.automation.scenario_name')" min-width="120px"
sortable/>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="name"
:label="$t('api_test.automation.scenario_name')"
min-width="120px"
sortable
/>
<ms-table-column
:field="item"
@ -59,20 +69,25 @@
prop="versionId"
:filters="versionFilters"
:label="$t('commons.version')"
min-width="120px">
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
min-width="120px"
>
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</ms-table-column>
<ms-table-column :field="item"
:fields-width="fieldsWidth"
prop="level" :label="$t('api_test.automation.case_level')" min-width="120px"
column-key="level"
sortable
:filters="apiscenariofilters.LEVEL_FILTERS">
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="level"
:label="$t('api_test.automation.case_level')"
min-width="120px"
column-key="level"
sortable
:filters="apiscenariofilters.LEVEL_FILTERS"
>
<template v-slot:default="scope">
<priority-table-item :value="scope.row.level" ref="level"/>
<priority-table-item :value="scope.row.level" ref="level" />
</template>
</ms-table-column>
@ -81,48 +96,68 @@
:fields-width="fieldsWidth"
prop="envs"
:label="$t('commons.environment')"
min-width="150">
<template v-slot:default="{row}">
min-width="150"
>
<template v-slot:default="{ row }">
<div v-if="row.envs">
<span v-for="(k, v, index) in row.envs" :key="index">
<span v-if="index===0 || index===1">
<span class="project-name" :title="v">{{ v }}</span>:
<span v-if="index === 0 || index === 1">
<span class="project-name" :title="v">{{ v }}</span
>:
<el-tag type="success" size="mini" effect="plain">
{{ k }}
</el-tag>
<br/>
<br />
</span>
<el-popover
placement="top"
width="350"
trigger="click">
<el-popover placement="top" width="350" trigger="click">
<div v-for="(k, v, index) in row.envs" :key="index">
<span class="plan-case-env">{{ v }}:
<el-tag type="success" size="mini" effect="plain">{{ k }}</el-tag><br/>
<span class="plan-case-env"
>{{ v }}:
<el-tag type="success" size="mini" effect="plain">{{
k
}}</el-tag
><br />
</span>
</div>
<el-link v-if="index === 2" slot="reference" type="info" :underline="false" icon="el-icon-more"/>
<el-link
v-if="index === 2"
slot="reference"
type="info"
:underline="false"
icon="el-icon-more"
/>
</el-popover>
</span>
</div>
</template>
</ms-table-column>
<ms-table-column :field="item"
:fields-width="fieldsWidth"
prop="tagNames" :label="$t('api_test.automation.tag')"
min-width="100px">
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="tagNames"
:label="$t('api_test.automation.tag')"
min-width="100px"
>
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" style="margin-left: 0px; margin-right: 2px"/>
<ms-tag
v-for="(itemName, index) in scope.row.tags"
:key="index"
type="success"
effect="plain"
:content="itemName"
style="margin-left: 0px; margin-right: 2px"
/>
</template>
</ms-table-column>
<ms-table-column :field="item"
:fields-width="fieldsWidth"
prop="createUser"
:label="$t('api_test.automation.creator')"
min-width="100px">
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="createUser"
:label="$t('api_test.automation.creator')"
min-width="100px"
>
<template v-slot:default="scope">
{{ scope.row.creatorName }}
</template>
@ -133,8 +168,9 @@
prop="userId"
:fields-width="fieldsWidth"
:label="$t('custom_field.case_maintainer')"
min-width="120">
<template v-slot:default="scope">
min-width="120"
>
<template v-slot:default="scope">
{{ scope.row.principalName }}
</template>
</ms-table-column>
@ -144,56 +180,89 @@
:fields-width="fieldsWidth"
prop="principal"
:label="$t('custom_field.case_maintainer')"
min-width="120"/>
min-width="120"
/>
<ms-update-time-column :field="item" :fields-width="fieldsWidth"/>
<ms-create-time-column :field="item" :fields-width="fieldsWidth"/>
<ms-update-time-column :field="item" :fields-width="fieldsWidth" />
<ms-create-time-column :field="item" :fields-width="fieldsWidth" />
<ms-table-column :field="item"
:fields-width="fieldsWidth"
prop="stepTotal" :label="$t('api_test.automation.step')"
min-width="80px"/>
<ms-table-column :field="item"
:fields-width="fieldsWidth"
prop="lastResult" min-width="100px"
:filters="apiscenariofilters.RESULT_FILTERS"
:label="$t('api_test.automation.last_result')">
<template v-slot:default="{row}">
<el-link @click="showReport(row)" :disabled="!row.lastResult || row.lastResult==='PENDING'">
<ms-test-plan-api-status :status="row.lastResult"/>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="stepTotal"
:label="$t('api_test.automation.step')"
min-width="80px"
/>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="lastResult"
min-width="100px"
:filters="apiscenariofilters.RESULT_FILTERS"
:label="$t('api_test.automation.last_result')"
>
<template v-slot:default="{ row }">
<el-link
@click="showReport(row)"
:disabled="!row.lastResult || row.lastResult === 'PENDING'"
>
<ms-test-plan-api-status :status="row.lastResult" />
</el-link>
</template>
</ms-table-column>
<ms-table-column :field="item"
:fields-width="fieldsWidth"
prop="passRate" min-width="80px"
:label="$t('api_test.automation.passing_rate')"/>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
prop="passRate"
min-width="80px"
:label="$t('api_test.automation.passing_rate')"
/>
</span>
</ms-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<ms-table-pagination
:change="search"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
/>
<div>
<!-- 执行结果 -->
<el-drawer :visible.sync="runVisible" :destroy-on-close="true" direction="ltr" :withHeader="true" :modal="false"
size="90%">
<el-drawer
:visible.sync="runVisible"
:destroy-on-close="true"
direction="ltr"
:withHeader="true"
:modal="false"
size="90%"
>
<!-- 接口场景报告 -->
<micro-app :to="`/automation/report/view/${reportId}`" service="api"/>
<micro-app
:to="`/automation/report/view/${reportId}`"
service="api"
/>
</el-drawer>
</div>
</el-card>
<!-- 批量编辑 -->
<batch-edit :dialog-title="$t('test_track.case.batch_edit_case')" :type-arr="typeArr" :value-arr="valueArr"
:select-row="this.$refs.table ? this.$refs.table.selectRows : new Set()" ref="batchEdit"
@batchEdit="batchEdit"/>
<ms-test-plan-run-mode-with-env @handleRunBatch="handleRunBatch" ref="runMode" :plan-case-ids="planCaseIds" :type="'apiScenario'"
@close="search" />
<batch-edit
:dialog-title="$t('test_track.case.batch_edit_case')"
:type-arr="typeArr"
:value-arr="valueArr"
:select-row="this.$refs.table ? this.$refs.table.selectRows : new Set()"
ref="batchEdit"
@batchEdit="batchEdit"
/>
<ms-test-plan-run-mode-with-env
@handleRunBatch="handleRunBatch"
ref="runMode"
:plan-case-ids="planCaseIds"
:type="'apiScenario'"
@close="search"
/>
<ms-task-center ref="taskCenter" :show-menu="false"/>
<ms-task-center ref="taskCenter" :show-menu="false" />
</div>
</template>
@ -201,29 +270,38 @@
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {getUUID, strMapToObj} from "metersphere-frontend/src/utils";
import {hasLicense, hasPermission} from "metersphere-frontend/src/utils/permission";
import {
getCurrentProjectID,
getCurrentWorkspaceId,
} from "metersphere-frontend/src/utils/token";
import { getUUID, strMapToObj } from "metersphere-frontend/src/utils";
import {
hasLicense,
hasPermission,
} from "metersphere-frontend/src/utils/permission";
import MsTableMoreBtn from "metersphere-frontend/src/components/table/TableMoreBtn";
import TestPlanScenarioListHeader from "./TestPlanScenarioListHeader";
import {
buildBatchParam,
getCustomTableHeader,
getCustomTableWidth,
initCondition
initCondition,
} from "metersphere-frontend/src/utils/tableUtils";
import {ENV_TYPE, TEST_PLAN_SCENARIO_CASE} from "metersphere-frontend/src/utils/constants";
import {
ENV_TYPE,
TEST_PLAN_SCENARIO_CASE,
} from "metersphere-frontend/src/utils/constants";
import HeaderLabelOperate from "metersphere-frontend/src/components/head/HeaderLabelOperate";
import BatchEdit from "@/business/case/components/BatchEdit";
import MsPlanRunMode from "@/business/plan/common/PlanRunModeWithEnv";
import PriorityTableItem from "@/business/common/tableItems/planview/PriorityTableItem";
import {API_SCENARIO_FILTERS} from "metersphere-frontend/src/utils/table-constants";
import { API_SCENARIO_FILTERS } from "metersphere-frontend/src/utils/table-constants";
import MsTaskCenter from "metersphere-frontend/src/components/task/TaskCenter";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import MsUpdateTimeColumn from "metersphere-frontend/src/components/table/MsUpdateTimeColumn";
import MsCreateTimeColumn from "metersphere-frontend/src/components/table/MsCreateTimeColumn";
import {editTestPlanScenarioCaseOrder} from "@/api/remote/plan/test-plan";
import { editTestPlanScenarioCaseOrder } from "@/api/remote/plan/test-plan";
import {
testPlanScenarioCaseBatchDelete,
testPlanScenarioCaseBatchUpdateEnv,
@ -231,14 +309,14 @@ import {
testPlanScenarioCaseRun,
testPlanScenarioCaseSelectAllTableRows,
testPlanScenarioEnv,
testPlanScenarioList
testPlanScenarioList,
} from "@/api/remote/plan/test-plan-scenario";
import {environmentGroupGetProjectMapName} from "@/api/environment-group";
import {apiAutomationReduction} from "@/api/remote/api/api-automation";
import { environmentGroupGetProjectMapName } from "@/api/environment-group";
import { apiAutomationReduction } from "@/api/remote/api/api-automation";
import MicroApp from "metersphere-frontend/src/components/MicroApp";
import MsTestPlanApiStatus from "@/business/plan/view/comonents/api/TestPlanApiStatus";
import {getVersionFilters} from "@/business/utils/sdk-utils";
import {TEST_PLAN_API_SCENARIO_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import { getVersionFilters } from "@/business/utils/sdk-utils";
import { TEST_PLAN_API_SCENARIO_CONFIGS } from "metersphere-frontend/src/components/search/search-components";
import MsTestPlanRunModeWithEnv from "@/business/plan/common/TestPlanRunModeWithEnv";
export default {
@ -277,13 +355,13 @@ export default {
return {
type: TEST_PLAN_SCENARIO_CASE,
tableHeaderKey: "TEST_PLAN_SCENARIO_CASE",
fields: getCustomTableHeader('TEST_PLAN_SCENARIO_CASE'),
fieldsWidth: getCustomTableWidth('TEST_PLAN_SCENARIO_CASE'),
screenHeight: 'calc(100vh - 250px)',//
fields: getCustomTableHeader("TEST_PLAN_SCENARIO_CASE"),
fieldsWidth: getCustomTableWidth("TEST_PLAN_SCENARIO_CASE"),
screenHeight: "calc(100vh - 250px)", //
tableLabel: [],
loading: false,
condition: {
components: TEST_PLAN_API_SCENARIO_CONFIGS
components: TEST_PLAN_API_SCENARIO_CONFIGS,
},
currentScenario: {},
schedule: {},
@ -294,57 +372,62 @@ export default {
pageSize: 10,
total: 0,
reportId: "",
status: 'default',
status: "default",
infoDb: false,
runVisible: false,
runData: [],
enableOrderDrag: true,
operators: [
{
tip: this.$t('api_test.run'), icon: "el-icon-video-play",
tip: this.$t("api_test.run"),
icon: "el-icon-video-play",
exec: this.execute,
class: this.planStatus === 'Archived' ? 'disable-run' : 'run-button',
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+RUN']
class: this.planStatus === "Archived" ? "disable-run" : "run-button",
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+RUN"],
},
{
tip: this.$t('test_track.plan_view.cancel_relevance'), icon: "el-icon-unlock",
tip: this.$t("test_track.plan_view.cancel_relevance"),
icon: "el-icon-unlock",
exec: this.remove,
type: 'danger',
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+RELEVANCE_OR_CANCEL']
}
type: "danger",
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+RELEVANCE_OR_CANCEL"],
},
],
buttons: [
{
name: this.$t('test_track.case.batch_unlink'),
name: this.$t("test_track.case.batch_unlink"),
handleClick: this.handleDeleteBatch,
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+CASE_BATCH_DELETE']
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+CASE_BATCH_DELETE"],
},
{
name: this.$t('api_test.automation.batch_execute'),
name: this.$t("api_test.automation.batch_execute"),
handleClick: this.handleBatchExecute,
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+CASE_BATCH_RUN']
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+CASE_BATCH_RUN"],
},
{
name: this.$t('test_track.case.batch_edit_case'),
name: this.$t("test_track.case.batch_edit_case"),
handleClick: this.handleBatchEdit,
isDisable: this.planStatus === 'Archived',
permissions: ['PROJECT_TRACK_PLAN:READ+CASE_BATCH_EDIT']
}
isDisable: this.planStatus === "Archived",
permissions: ["PROJECT_TRACK_PLAN:READ+CASE_BATCH_EDIT"],
},
],
typeArr: [
{id: 'projectEnv', name: this.$t('api_test.definition.request.run_env')},
{
id: "projectEnv",
name: this.$t("api_test.definition.request.run_env"),
},
],
valueArr: {
projectEnv: []
projectEnv: [],
},
planCaseIds: [],
apiscenariofilters: {},
versionFilters: [],
}
};
},
computed: {
projectId() {
@ -352,7 +435,7 @@ export default {
},
editTestPlanScenarioCaseOrder() {
return editTestPlanScenarioCaseOrder;
}
},
},
created() {
this.apiscenariofilters = API_SCENARIO_FILTERS();
@ -366,7 +449,7 @@ export default {
},
planId() {
this.search();
}
},
},
methods: {
hasPermission,
@ -380,79 +463,83 @@ export default {
this.loading = true;
this.condition.moduleIds = this.selectNodeIds;
if (this.clickType) {
if (this.status == 'default') {
if (this.status == "default") {
this.condition.status = this.clickType;
} else {
this.condition.status = null;
}
this.status = 'all';
this.status = "all";
}
this.enableOrderDrag = (this.condition.orders && this.condition.orders.length) > 0 ? false : true;
this.enableOrderDrag =
(this.condition.orders && this.condition.orders.length) > 0
? false
: true;
if (this.planId) {
this.condition.planId = this.planId;
testPlanScenarioList({pageNum: this.currentPage, pageSize: this.pageSize}, this.condition)
.then(response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
this.tableData.forEach(item => {
try {
let envs;
const type = item.environmentType;
if (type === ENV_TYPE.GROUP && item.environmentGroupId) {
environmentGroupGetProjectMapName(item.environmentGroupId)
.then(res => {
let data = res.data;
if (data) {
envs = new Map(Object.entries(data));
this.$set(item, 'envs', res.data);
}
});
} else if (type === ENV_TYPE.JSON) {
envs = JSON.parse(item.environment);
if (envs) {
testPlanScenarioEnv(envs)
.then(res => {
this.$set(item, 'envs', res.data);
});
}
}
} catch (error) {
this.$set(item, 'envs', {});
}
})
this.loading = false;
testPlanScenarioList(
{ pageNum: this.currentPage, pageSize: this.pageSize },
this.condition
).then((response) => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach((item) => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
this.tableData.forEach((item) => {
try {
let envs;
const type = item.environmentType;
if (type === ENV_TYPE.GROUP && item.environmentGroupId) {
environmentGroupGetProjectMapName(item.environmentGroupId).then(
(res) => {
let data = res.data;
if (data) {
envs = new Map(Object.entries(data));
this.$set(item, "envs", res.data);
}
}
);
} else if (type === ENV_TYPE.JSON) {
envs = JSON.parse(item.environment);
if (envs) {
testPlanScenarioEnv(envs).then((res) => {
this.$set(item, "envs", res.data);
});
}
}
} catch (error) {
this.$set(item, "envs", {});
}
});
this.loading = false;
});
}
},
reductionApi(row) {
row.scenarioDefinition = null;
let rows = [row];
apiAutomationReduction(rows)
.then(response => {
this.$success(this.$t('commons.save_success'));
this.search();
});
apiAutomationReduction(rows).then((response) => {
this.$success(this.$t("commons.save_success"));
this.search();
});
},
handleBatchExecute() {
let rows = this.orderBySelectRows(this.$refs.table.selectRows);
this.planCaseIds = [];
rows.forEach(row => {
rows.forEach((row) => {
this.planCaseIds.push(row.id);
})
});
//this.planCaseIds
this.$nextTick(()=>{
this.$refs.runMode.open('API');
})
this.$nextTick(() => {
this.$refs.runMode.open("API");
});
},
orderBySelectRows(rows) {
let selectIds = Array.from(rows).map(row => row.id);
let selectIds = Array.from(rows).map((row) => row.id);
let array = [];
for (let i in this.tableData) {
if (selectIds.indexOf(this.tableData[i].id) !== -1) {
@ -465,9 +552,9 @@ export default {
let rows = this.orderBySelectRows(this.$refs.table.selectRows);
if (this.planId) {
let selectParam = buildBatchParam(this);
let param = {config: config, planCaseIds: []};
param.ids = rows.map(r => r.id);
rows.forEach(row => {
let param = { config: config, planCaseIds: [] };
param.ids = rows.map((r) => r.id);
rows.forEach((row) => {
this.buildExecuteParam(param, row);
});
param.config.envMap = strMapToObj(config.envMap);
@ -475,24 +562,23 @@ export default {
param.triggerMode = "BATCH";
param.requestOriginator = "TEST_PLAN";
param.projectId = this.projectId;
testPlanScenarioCaseRun(param)
.then(() => {
this.$message(this.$t('commons.run_message'));
this.$refs.taskCenter.open();
this.search();
});
testPlanScenarioCaseRun(param).then(() => {
this.$message(this.$t("commons.run_message"));
this.$refs.taskCenter.open();
this.search();
});
}
},
execute(row) {
this.infoDb = false;
let param = {planCaseIds: []};
let param = { planCaseIds: [] };
this.reportId = "";
this.buildExecuteParam(param, row);
param.triggerMode = "MANUAL";
param.requestOriginator = "TEST_PLAN";
param.projectId = this.projectId;
param.config = {
mode: "serial"
mode: "serial",
};
if (this.planId) {
this.loading = true;
@ -505,10 +591,11 @@ export default {
this.reportId = response.data[0].reportId;
}
this.search();
}, 2000)
}).catch(() => {
this.loading = false;
});
}, 2000);
})
.catch(() => {
this.loading = false;
});
}
},
buildExecuteParam(param, row) {
@ -526,48 +613,57 @@ export default {
},
remove(row) {
if (this.planId) {
testPlanScenarioCaseDelete(row.id)
.then(() => {
this.$success(this.$t('test_track.cancel_relevance_success'));
this.$emit('refresh');
this.search();
});
testPlanScenarioCaseDelete(row.id).then(() => {
this.$success(this.$t("test_track.cancel_relevance_success"));
this.$emit("refresh");
this.search();
});
}
},
handleDeleteBatch() {
this.$alert(this.$t('test_track.plan_view.confirm_cancel_relevance') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let param = buildBatchParam(this);
param.ids = this.$refs.table.selectIds;
if (this.planId) {
param.planId = this.planId;
testPlanScenarioCaseBatchDelete(param)
.then(() => {
this.$alert(
this.$t("test_track.plan_view.confirm_cancel_relevance") + "",
"",
{
confirmButtonText: this.$t("commons.confirm"),
callback: (action) => {
if (action === "confirm") {
let param = buildBatchParam(this);
param.ids = this.$refs.table.selectIds;
if (this.planId) {
param.planId = this.planId;
testPlanScenarioCaseBatchDelete(param).then(() => {
this.$refs.table.selectRows.clear();
this.search();
this.$success(this.$t('test_track.cancel_relevance_success'));
this.$emit('refresh');
this.$success(this.$t("test_track.cancel_relevance_success"));
this.$emit("refresh");
});
}
}
}
},
}
});
);
},
handleBatchEdit() {
if (this.condition != null && this.condition.selectAll) {
let selectAllRowParams = buildBatchParam(this);
selectAllRowParams.ids = this.$refs.table.selectIds;
testPlanScenarioCaseSelectAllTableRows(selectAllRowParams)
.then(response => {
testPlanScenarioCaseSelectAllTableRows(selectAllRowParams).then(
(response) => {
let dataRows = response.data;
this.$refs.batchEdit.open(dataRows.size);
this.$refs.batchEdit.setScenarioSelectRows(dataRows, "planScenario");
});
this.$refs.batchEdit.setScenarioSelectRows(
dataRows,
"planScenario"
);
}
);
} else {
this.$refs.batchEdit.open(this.$refs.table.selectRows.size);
this.$refs.batchEdit.setScenarioSelectRows(this.$refs.table.selectRows, "planScenario");
this.$refs.batchEdit.setScenarioSelectRows(
this.$refs.table.selectRows,
"planScenario"
);
}
},
batchEdit(form) {
@ -577,29 +673,37 @@ export default {
param.environmentType = form.environmentType;
param.envGroupId = form.envGroupId;
if (this.planId) {
testPlanScenarioCaseBatchUpdateEnv(param)
.then(() => {
this.$success(this.$t('commons.save_success'));
this.search();
});
testPlanScenarioCaseBatchUpdateEnv(param).then(() => {
this.$success(this.$t("commons.save_success"));
this.search();
});
}
},
getVersionOptions() {
if (hasLicense()) {
getVersionFilters(getCurrentProjectID())
.then(r => this.versionFilters = r.data);
getVersionFilters(getCurrentProjectID()).then(
(r) => (this.versionFilters = r.data)
);
}
},
openById(item) {
let automationData = this.$router.resolve('/api/automation/' + getUUID() + "/scenario/edit:" + item.caseId + "/" + item.projectId + "/" + getCurrentWorkspaceId());
window.open(automationData.href, '_blank');
let automationData = this.$router.resolve(
"/api/automation/default/" +
getUUID() +
"/scenario/edit:" +
item.caseId +
"/" +
item.projectId +
"/" +
getCurrentWorkspaceId()
);
window.open(automationData.href, "_blank");
},
}
}
},
};
</script>
<style scoped>
.plan-case-env {
display: inline-block;
padding: 0 0;
@ -619,5 +723,4 @@ export default {
width: 80px;
vertical-align: middle;
}
</style>

View File

@ -1,21 +1,5 @@
<template>
<div v-loading="loading">
<env-group-popover
:env-map="projectEnvMap"
:project-ids="projectIds"
:show-env-group="false"
@setProjectEnvMap="setProjectEnvMap"
:environment-type.sync="environmentType"
:group-id="envGroupId"
:is-scenario="false"
@setEnvGroup="setEnvGroup"
:show-config-button-with-out-permission="
showConfigButtonWithOutPermission
"
:project-list="projectList"
ref="envPopover"
class="env-popover"
/>
<ms-table-adv-search-bar :condition.sync="condition" class="adv-search-bar"
v-if="condition.components !== undefined && condition.components.length > 0"
@ -115,9 +99,6 @@ import {
getCustomTableWidth
} from "metersphere-frontend/src/utils/tableUtils";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
import {getApiScenarioEnvByProjectId} from "@/api/remote/api/api-automation";
import {getUiScenarioEnvByProjectId} from "@/api/remote/ui/ui-automation";
export default {
name: "RelevanceUiScenarioList",
@ -131,7 +112,6 @@ export default {
MsTag,
MsTableAdvSearchBar,
MsTableColumn,
EnvGroupPopover,
},
props: {
referenced: {
@ -167,7 +147,6 @@ export default {
envGroupId: "",
versionFilters: [],
fieldsWidth: getCustomTableWidth('TEST_PLAN_UI_SCENARIO_CASE'),
projectIds: new Set()
};
},
computed: {
@ -265,22 +244,10 @@ export default {
selectCountChange(data) {
this.selectRows = this.$refs.scenarioTable.selectRows;
this.$emit("selectCountChange", data);
this.initProjectIds();
},
showReport() {
},
initProjectIds() {
this.projectIds.clear();
// this.map.clear();
this.selectRows.forEach((row) => {
getUiScenarioEnvByProjectId(row.id).then((res) => {
let data = res.data;
data.projectIds.forEach((d) => this.projectIds.add(d));
// this.map.set(row.id, data.projectIds);
});
});
},
}
}
};
</script>

View File

@ -148,10 +148,6 @@ export default {
let map = this.$refs.apiScenarioList.map;
let envGroupId = this.$refs.apiScenarioList.envGroupId;
if (!envMap || envMap.size == 0) {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}
selectRows.forEach(row => {
selectIds.push(row.id);
})
@ -183,6 +179,8 @@ export default {
this.autoCheckStatus();
this.$refs.baseRelevance.close();
});
},
autoCheckStatus() { //
if (!this.planId) {

View File

@ -147,8 +147,7 @@
:filters="apiscenariofilters.RESULT_FILTERS"
:label="$t('api_test.automation.last_result')">
<template v-slot:default="{row}">
<el-link @click="showReport(row)"
:disabled="!row.lastResult || row.lastResult==='PENDING' || row.lastResult==='UnExecute'">
<el-link @click="showReport(row)" :disabled="!row.lastResult || row.lastResult==='PENDING' || row.lastResult==='UnExecute'">
<ms-test-plan-api-status :status="row.lastResult==='UnExecute' ? 'PENDING' : row.lastResult"/>
</el-link>
</template>
@ -177,8 +176,8 @@
:select-row="this.$refs.table ? this.$refs.table.selectRows : new Set()" ref="batchEdit"
@batchEdit="batchEdit"/>
<ui-run-mode @handleRunBatch="handleRunBatch" ref="runMode" :custom-run-mode="true"
:custom-serial-on-sample-error="true" :request="conditionRequest"/>
<ui-run-mode @handleRunBatch="handleRunBatch" ref="runMode" :custom-run-mode="true"
:custom-serial-on-sample-error="true"/>
<ms-task-center ref="taskCenter" :show-menu="false"/>
</div>
@ -328,8 +327,6 @@ export default {
]
},
versionFilters: [],
//
conditionRequest: {}
}
},
computed: {
@ -361,14 +358,14 @@ export default {
},
search() {
initCondition(this.condition, this.condition.selectAll);
if (this.condition && this.condition.filters && this.condition.filters.last_result) {
if (this.condition.filters.last_result.length > 0) {
if(this.condition && this.condition.filters && this.condition.filters.last_result){
if(this.condition.filters.last_result.length > 0){
//PENDING
if (this.condition.filters.last_result.includes("PENDING")) {
if(this.condition.filters.last_result.includes("PENDING")){
this.condition.filters.last_result = [...this.condition.filters.last_result, "UnExecute"]
}
//ERROR
if (this.condition.filters.last_result.includes("ERROR")) {
if(this.condition.filters.last_result.includes("ERROR")){
this.condition.filters.last_result = [...this.condition.filters.last_result, "FAIL"]
}
}
@ -440,19 +437,12 @@ export default {
let rows = this.orderBySelectRows(this.$refs.table.selectRows);
this.planCaseIds = [];
rows.forEach(row => {
this.planCaseIds.push(row.caseId);
this.planCaseIds.push(row.id);
})
this.conditionRequest.id = getUUID();
this.conditionRequest.ids = this.planCaseIds;
this.conditionRequest.projectId = this.projectId;
this.conditionRequest.condition = this.condition;
this.$refs.runMode.open();
},
orderBySelectRows(rows) {
let selectIds = this.$refs.table.selectIds;
if (rows) {
selectIds = Array.from(rows).map(row => row.id);
}
let selectIds = Array.from(rows).map(row => row.id);
let array = [];
for (let i in this.tableData) {
if (selectIds.indexOf(this.tableData[i].id) !== -1) {
@ -509,8 +499,8 @@ export default {
},
},
},
this.$t("ui.view_config")
),
this.$t("ui.view_config")
),
])
);
validate = false;

View File

@ -7,21 +7,6 @@
:visible.sync="runModeVisible"
>
<div class="mode-container">
<div>
<div>{{ $t("commons.environment") }}</div>
<env-select-popover :project-ids="projectIds"
:project-list="projectList"
:project-env-map="projectEnvListMap"
:environment-type="'JSON'"
:has-option-group="false"
:group-id="runConfig.environmentGroupId"
@setProjectEnvMap="setProjectEnvMap"
ref="envSelectPopover"
class="mode-row"
></env-select-popover>
</div>
<!-- 浏览器 -->
<div class="browser-row wrap">
<div class="title">{{ $t("ui.browser") }}</div>
@ -190,13 +175,11 @@
<script>
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter'
import {getCurrentProjectID, getOwnerProjects, strMapToObj} from "@/business/utils/sdk-utils";
import {uiScenarioEnvMap} from "@/api/remote/ui/ui-automation";
import EnvSelectPopover from "@/business/plan/env/EnvSelectPopover";
import {getOwnerProjects} from "@/business/utils/sdk-utils";
export default {
name: "UiRunMode",
components: {MsDialogFooter, EnvSelectPopover},
components: {MsDialogFooter},
data() {
return {
runModeVisible: false,
@ -224,8 +207,6 @@ export default {
},
projectList: [],
projectIds: new Set(),
projectEnvListMap: {},
caseIdEnvNameMap: {},
};
},
props: {
@ -281,7 +262,6 @@ export default {
};
this.runModeVisible = true;
this.getWsProjects();
this.showPopover();
},
changeMode() {
this.runConfig.runWithinResourcePool = false;
@ -316,29 +296,6 @@ export default {
this.$emit("handleRunBatch", this.runConfig);
this.close();
},
setProjectEnvMap(projectEnvMap) {
this.runConfig.envMap = strMapToObj(projectEnvMap);
},
showPopover() {
this.showScenarioPopover();
},
showScenarioPopover() {
let currentProjectID = getCurrentProjectID();
this.projectIds.clear();
uiScenarioEnvMap(this.request).then((res) => {
let data = res.data;
this.projectEnvListMap = data;
if (data) {
for (let d in data) {
this.projectIds.add(d);
}
}
if (this.projectIds.size === 0) {
this.projectIds.add(currentProjectID);
}
this.$refs.envSelectPopover.open();
});
},
},
};
</script>

View File

@ -88,7 +88,6 @@ const TRACK_HEADER = {
{id: 'name', key: '2', label: 'api_test.automation.scenario_name'},
{id: 'versionId', key: 'd', label: 'commons.version'},
{id: 'level', key: '3', label: 'api_test.automation.case_level'},
{id: 'envs', key: '8', label: 'commons.environment'},
{id: 'tagNames', key: '4', label: 'api_test.automation.tag'},
{id: 'stepTotal', key: '7', label: 'api_test.automation.step'},
{id: 'passRate', key: '9', label: 'api_test.automation.passing_rate'},