feat(测试计划): 高级搜索中添加用例状态和需求过滤条件

--story=1010335 --user=李玉号 【测试计划】测试计划中关联测试用例时 高级筛选条件中增加用例状态过滤 https://www.tapd.cn/55049933/s/1289108
This commit is contained in:
shiziyuan9527 2022-11-03 15:32:28 +08:00 committed by 刘瑞斌
parent a66a70e7d8
commit 5dfcafd6d1
13 changed files with 247 additions and 38 deletions

View File

@ -223,6 +223,12 @@
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.case_status != null">
and t1.case_status
<include refid="condition">
<property name="object" value="${condition}.case_status"/>
</include>
</if>
<if test="${condition}.exec_result != null">
and (t1.status
<choose>

View File

@ -158,6 +158,13 @@
</if>
</foreach>
</if>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<include refid="queryVersionCondition">
<property name="versionTable" value="a"/>
</include>
@ -179,6 +186,52 @@
</if>
</select>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and c.name
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and t.update_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.createTime != null">
and t.create_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.priority != null">
and c.priority
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.priority"/>
</include>
</if>
<if test="${condition}.path != null">
and c.path
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.path"/>
</include>
</if>
<if test="${condition}.status != null">
and c.status
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.case_status != null">
and c.case_status
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.case_status"/>
</include>
</if>
</sql>
<select id="selectIds" resultType="java.lang.String">
select
t.id

View File

@ -117,6 +117,13 @@
</if>
<include refid="filters"/>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="objectKey" value="request.combine.tags"/>
</include>
</if>
<if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">
@ -131,7 +138,38 @@
</foreach>
</if>
</select>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and c.name
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and t.update_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.createTime != null">
and t.create_time
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.priority != null">
and c.priority
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.priority"/>
</include>
</if>
<if test="${condition}.status != null">
and c.status
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
</sql>
<select id="selectByIds" resultType="io.metersphere.base.domain.TestPlanApiScenario">
select t.*
from test_plan_api_scenario t

View File

@ -38,6 +38,7 @@ export default {
if (!projectId) {
projectId = getCurrentUser().lastProjectId;
}
let originUrl = this.component.options.url;
this.component.options.url += '/' + projectId;
this.loading = get(this.component.options.url).then(response => {
if (response.data) {
@ -48,6 +49,7 @@ export default {
})
})
}
this.component.options.url = originUrl;
})
}
},

View File

@ -218,6 +218,23 @@ export const API_STATUS = {
}
}
export const CASE_STATUS = {
key: "case_status",
name: 'MsTableSearchSelect',
label: 'commons.status',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{value: 'Prepare', label: 'test_track.plan.plan_status_prepare'},
{value: 'Underway', label: 'test_track.plan.plan_status_running'},
{value: 'Completed', label: 'test_track.plan.plan_status_completed'}
],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_STATUS_TRASH = {
key: "status",
name: 'MsTableSearchSelect',
@ -869,13 +886,15 @@ export const API_SCENARIO_CONFIGS_TRASH = [ID, NAME, PRIORITY, TAGS, API_SCENARI
export const TEST_PLAN_REPORT_CONFIGS = [NAME, TEST_PLAN_NAME, CREATOR, CREATE_TIME, TEST_PLAN_TRIGGER_MODE, TEST_PLAN_REPORT_STATUS];
// 测试计划 功能用例
export const TEST_PLAN_TEST_CASE_CONFIGS = [NAME, TAGS, MODULE, PRIORITY, CREATE_TIME, UPDATE_TIME, EXECUTOR, CASE_REVIEW_STATUS, PLAN_CASE_STATUS, PRINCIPAL];
export const TEST_PLAN_TEST_CASE_CONFIGS = [NAME, TAGS, MODULE, PRIORITY, CREATE_TIME, UPDATE_TIME, EXECUTOR, CASE_REVIEW_STATUS, PLAN_CASE_STATUS, PRINCIPAL, CASE_DEMAND];
export const TEST_PLAN_API_CASE_CONFIGS = [NAME, CASE_STATUS, CREATE_TIME, UPDATE_TIME];
export const TEST_PLAN_API_SCENARIO_CONFIGS = [NAME, API_STATUS, CREATE_TIME, UPDATE_TIME];
// 测试计划关联页面
export const TEST_PLAN_RELEVANCE_FUNC_CONFIGS = [NAME, TAGS, CREATE_TIME, UPDATE_TIME, CREATOR];
export const TEST_PLAN_RELEVANCE_API_DEFINITION_CONFIGS = [NAME, API_METHOD, API_PATH, TAGS, UPDATE_TIME, CREATE_TIME, CREATOR];
export const TEST_PLAN_RELEVANCE_API_CASE_CONFIGS = [NAME, PRIORITY, TAGS, UPDATE_TIME, CREATOR, API_PATH];
export const TEST_PLAN_RELEVANCE_API_SCENARIO_CONFIGS = [NAME, PRIORITY, TAGS, API_SCENARIO_RESULT, CREATE_TIME, UPDATE_TIME, CREATOR];
export const TEST_PLAN_RELEVANCE_API_DEFINITION_CONFIGS = [NAME, API_METHOD, API_PATH, TAGS, UPDATE_TIME, CREATE_TIME, CREATOR, API_STATUS];
export const TEST_PLAN_RELEVANCE_API_CASE_CONFIGS = [NAME, PRIORITY, TAGS, UPDATE_TIME, CREATOR, API_PATH, CASE_STATUS];
export const TEST_PLAN_RELEVANCE_API_SCENARIO_CONFIGS = [NAME, PRIORITY, TAGS, API_SCENARIO_RESULT, CREATE_TIME, UPDATE_TIME, CREATOR, API_STATUS];
export const TEST_PLAN_RELEVANCE_UI_SCENARIO_CONFIGS = [NAME, PRIORITY, TAGS, UI_SCENARIO_RESULT, CREATE_TIME, UPDATE_TIME, CREATOR];
export const TEST_PLAN_RELEVANCE_LOAD_CASE = [NAME, STATUS, CREATE_TIME, UPDATE_TIME, CREATOR];

View File

@ -132,6 +132,47 @@
<property name="object" value="${condition}.planCaseStatus"/>
</include>
</if>
<if test="${condition}.demand != null">
<if test="${condition}.demand.operator == 'third_platform'">
and test_case.demand_id in
<foreach collection="${condition}.demand.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</if>
<if test="${condition}.demand.operator == 'other_platform'">
and test_case.demand_name like CONCAT('%', #{${condition}.demand.value},'%')
</if>
</if>
<if test="${condition}.customs != null and ${condition}.customs.size() > 0">
<foreach collection="${condition}.customs" item="custom" separator="" open="" close="">
and test_case.id in (
select resource_id from custom_field_test_case where field_id = #{custom.id}
<choose>
<when test="custom.type == 'multipleMember' or custom.type == 'checkbox' or custom.type == 'multipleSelect'">
and ${custom.value}
</when>
<when test="custom.type == 'date' or custom.type == 'datetime'">
and left(replace(unix_timestamp(trim(both '"' from `value`)), '.', ''), 13)
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</when>
<when test="custom.type == 'richText' or custom.type == 'textarea'">
and text_value
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</when>
<otherwise>
and trim(both '"' from value)
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.condition">
<property name="object" value="custom"/>
</include>
</otherwise>
</choose>
)
</foreach>
</if>
</sql>

View File

@ -30,4 +30,6 @@ public class TestPlanCaseDTO extends TestCaseWithBLOBs {
private List<io.metersphere.dto.TestCaseTestDTO> list;
private List<IssuesDao> issueList;
private List<CustomFieldDao> fields;
}

View File

@ -32,10 +32,7 @@ import io.metersphere.plan.utils.TestPlanStatusCalculator;
import io.metersphere.request.OrderRequest;
import io.metersphere.request.ResetOrderRequest;
import io.metersphere.request.member.QueryMemberRequest;
import io.metersphere.service.BaseProjectApplicationService;
import io.metersphere.service.BaseUserService;
import io.metersphere.service.FunctionCaseExecutionInfoService;
import io.metersphere.service.ServiceUtils;
import io.metersphere.service.*;
import io.metersphere.dto.*;
import io.metersphere.request.testcase.TrackCount;
import io.metersphere.request.testreview.SaveCommentRequest;
@ -89,6 +86,8 @@ public class TestPlanTestCaseService {
private BaseProjectApplicationService baseProjectApplicationService;
@Resource
private FunctionCaseExecutionInfoService functionCaseExecutionInfoService;
@Resource
private CustomFieldTestCaseService customFieldTestCaseService;
private static final String CUSTOM_NUM = "custom_num";
private static final String NUM = "num";
@ -105,6 +104,9 @@ public class TestPlanTestCaseService {
public List<TestPlanCaseDTO> list(QueryTestPlanCaseRequest request) {
List<TestPlanCaseDTO> list = extTestPlanTestCaseMapper.list(request);
Map<String, List<CustomFieldDao>> fieldMap =
customFieldTestCaseService.getMapByResourceIds(list.stream().map(TestPlanCaseDTO::getCaseId).collect(Collectors.toList()));
list.forEach(i -> i.setFields(fieldMap.get(i.getCaseId())));
if (CollectionUtils.isNotEmpty(list)) {
// 设置版本信息
ServiceUtils.buildVersionInfo(list);

View File

@ -12,14 +12,14 @@
ref="envPopover" class="env-popover"/>
<el-input :placeholder="$t('api_test.definition.request.select_case')" @blur="filterSearch"
@keyup.enter.native="filterSearch" class="search-input" size="small" v-model="condition.name"/>
<ms-table-adv-search-bar :condition.sync="condition" class="adv-search-bar"
v-if="condition.components !== undefined && condition.components.length > 0"
@search="filterSearch"/>
<mx-version-select v-xpack :project-id="projectId" @changeVersion="changeVersion" style="float: left;"
class="search-input"/>
<ms-search
:base-search-tip="$t('api_test.definition.request.select_case')"
:condition.sync="condition"
style="margin-top: 10px"
@search="filterSearch">
</ms-search>
<ms-table ref="scenarioTable"
v-loading="result.loading"
:data="tableData"
@ -100,9 +100,10 @@ import MsTag from "metersphere-frontend/src/components/MsTag";
import TestPlanScenarioListHeader from "./TestPlanScenarioListHeader";
import PriorityTableItem from "@/business/common/tableItems/planview/PriorityTableItem";
import MsTableAdvSearchBar from "metersphere-frontend/src/components/search/MsTableAdvSearchBar";
import TEST_PLAN_RELEVANCE_API_SCENARIO_CONFIGS from "metersphere-frontend/src/components/search/search-components";
import {TEST_PLAN_RELEVANCE_API_SCENARIO_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsSearch from "metersphere-frontend/src/components/search/MsSearch";
import {getOwnerProjects, getVersionFilters} from "@/business/utils/sdk-utils";
import MxVersionSelect from "metersphere-frontend/src/components/version/MxVersionSelect";
import {getProjectApplicationConfig} from "@/api/project-application";
@ -122,7 +123,8 @@ export default {
MsTag,
// MsApiReportDetail,
MsTableAdvSearchBar,
MxVersionSelect
MxVersionSelect,
MsSearch
},
props: {
referenced: {

View File

@ -220,6 +220,7 @@ import {
} 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";
export default {
name: "TestPlanApiCaseList",
@ -252,7 +253,9 @@ export default {
fields: getCustomTableHeader('TEST_PLAN_API_CASE'),
fieldsWidth: getCustomTableWidth('TEST_PLAN_API_CASE'),
tableLabel: [],
condition: {},
condition: {
components: TEST_PLAN_API_CASE_CONFIGS
},
selectCase: {},
loading: false,
moduleId: "",

View File

@ -241,6 +241,7 @@ 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";
export default {
name: "MsTestPlanApiScenarioList",
@ -282,7 +283,9 @@ export default {
screenHeight: 'calc(100vh - 250px)',//
tableLabel: [],
loading: false,
condition: {},
condition: {
components: TEST_PLAN_API_SCENARIO_CONFIGS
},
currentScenario: {},
schedule: {},
selectAll: false,

View File

@ -121,7 +121,8 @@ import TestPlanCaseStatusTableItem from "@/business/common/tableItems/TestPlanCa
import {TEST_CASE_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import MxVersionSelect from "metersphere-frontend/src/components/version/MxVersionSelect";
import {getProjectApplicationConfig} from "@/api/project-application";
import {getVersionFilters} from "@/business/utils/sdk-utils";
import {getAdvSearchCustomField, getVersionFilters} from "@/business/utils/sdk-utils";
import {getTestTemplate} from "@/api/custom-field-template";
export default {
name: "FunctionalRelevance",
@ -167,7 +168,8 @@ export default {
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
versionFilters: null
versionFilters: null,
testCaseTemplate: {},
};
},
props: {
@ -199,7 +201,8 @@ export default {
selectNodeIds() {
this.getTestCases();
},
projectId() {
projectId(val) {
this.pushCustomFieldToCondition(val);
this.setConditionModuleIdParam();
this.page.condition.projectId = this.projectId;
this.page.condition.versionId = null;
@ -211,7 +214,7 @@ export default {
},
methods: {
open() {
this.page.condition = {components: TEST_CASE_CONFIGS};
this.page.condition = {custom: false, components: TEST_CASE_CONFIGS};
this.isSaving = false;
this.$refs.baseRelevance.open();
if (this.$refs.table) {
@ -300,7 +303,23 @@ export default {
},
setSelectCounts(data) {
this.$refs.baseRelevance.selectCounts = data;
}
},
pushCustomFieldToCondition(projectId) {
getTestTemplate(projectId).then(data => {
this.testCaseTemplate = data;
let comp = getAdvSearchCustomField(this.page.condition, this.testCaseTemplate.customFields);
let caseStatus = comp.find(i => i.label === '用例状态');
if (caseStatus) {
caseStatus.label = this.$t('custom_field.case_status');
caseStatus.options.forEach(option => {
option.text = this.$t(option.text);
});
caseStatus.custom = false;
this.page.condition.custom = false;
this.page.condition.components.push(caseStatus);
}
});
},
}
};
</script>

View File

@ -35,6 +35,7 @@
:row-order-group-id="planId"
:row-order-func="editTestPlanTestCaseOrder"
:enable-order-drag="enableOrderDrag"
:custom-fields="testCaseTemplate.customFields"
@filter="search"
@order="initTableData"
@handlePageChange="initTableData"
@ -230,6 +231,9 @@
<priority-table-item
:value="getCustomFieldValue(scope.row, field) ? getCustomFieldValue(scope.row, field) : scope.row.priority"/>
</span>
<span v-else-if="field.name === '用例状态'">
{{ getCustomFieldValue(scope.row, field, scope.row.status) }}
</span>
<span v-else>
{{ getCustomFieldValue(scope.row, field) }}
</span>
@ -290,7 +294,9 @@ import {
getLastTableSortField,
getTableHeaderWithCustomFields,
initCondition,
getCustomFieldFilter
getCustomFieldFilter,
parseCustomFilesForList,
getCustomFieldValue as _getCustomFieldValue,
} from "metersphere-frontend/src/utils/tableUtils";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
@ -305,14 +311,18 @@ import {
import {SYSTEM_FIELD_NAME_MAP} from "metersphere-frontend/src/utils/table-constants";
import {getTestPlanTestCase} from "@/api/testCase";
import TestPlanCaseIssueItem from "@/business/plan/view/comonents/functional/TestPlanCaseIssueItem";
import {getCustomFieldValueForTrack, getProjectMemberOption, getProjectVersions} from "@/business/utils/sdk-utils";
import {
getProjectMemberOption,
getProjectVersions,
getAdvSearchCustomField
} from "@/business/utils/sdk-utils";
import {
testPlanTestCaseBatchDelete,
testPlanTestCaseBatchEdit,
testPlanTestCaseDelete,
testPlanTestCaseEdit
} from "@/api/remote/plan/test-plan-test-case";
import {getIssuesByCaseId, getOriginIssuesByCaseId} from "@/api/issue";
import {getOriginIssuesByCaseId} from "@/api/issue";
export default {
name: "FunctionalTestCaseList",
@ -341,7 +351,8 @@ export default {
result: {},
deletePath: "/test/case/delete",
condition: {
components: TEST_PLAN_TEST_CASE_CONFIGS
components: TEST_PLAN_TEST_CASE_CONFIGS,
custom: false
},
nextPageData: null,
prePageData: null,
@ -518,7 +529,7 @@ export default {
loadIssue(row) {
if (row.issuesSize && !row.hasLoadIssue) {
getOriginIssuesByCaseId('PLAN_FUNCTIONAL', row.id)
.then(r => {
.then(r => {
this.$set(row, "issuesContent", r.data);
this.$set(row, "hasLoadIssue", true);
});
@ -551,7 +562,19 @@ export default {
Promise.all([p1, p2]).then((data) => {
let template = data[1];
this.testCaseTemplate = template;
this.testCaseTemplate.customFields = this.testCaseTemplate.customFields.filter(item => item.name === '用例状态' && item.system);
this.fields = getTableHeaderWithCustomFields(this.tableHeaderKey, this.testCaseTemplate.customFields);
let comp = getAdvSearchCustomField(this.condition, this.testCaseTemplate.customFields);
let caseStatus = comp.find(i => i.label === '用例状态');
if (caseStatus) {
caseStatus.label = this.$t('custom_field.case_status');
caseStatus.options.forEach(option => {
option.text = this.$t(option.text);
});
caseStatus.custom = false;
this.condition.custom = false;
this.condition.components.push(caseStatus);
}
this.$nextTick(() => {
if (this.$refs.table) {
this.$refs.table.resetHeader();
@ -562,23 +585,18 @@ export default {
},
getCustomFieldFilter(field) {
if (field.name === '用例状态') {
let option = [];
field.options.forEach((item) => {
option.push({
text: this.$t(item.text),
value: item.value
})
});
return option;
return null;
}
return getCustomFieldFilter(field, this.userFilters);
},
getCustomFieldValue(row, field, defaultVal = '') {
let value = getCustomFieldValueForTrack(row, field, this.members);
let value = _getCustomFieldValue(row, field, this.members);
if (field.name === '用例等级') {
return row.priority;
} else if (field.name === '责任人') {
return row.maintainerName;
} else if (field.name === '用例状态') {
value = value === 'Trash' ? this.$t('test_track.plan.plan_status_trash') : value
}
return value ? value : defaultVal;
},
@ -610,6 +628,7 @@ export default {
this.total = r.data.itemCount;
this.pageCount = Math.ceil(this.total / this.pageSize);
this.tableData = r.data.listObject;
parseCustomFilesForList(this.tableData);
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i]) {
if (this.tableData[i].customFields) {