feat(测试计划): 测试计划详情-接口用例页面联调和取消关联

This commit is contained in:
teukkk 2024-06-07 19:23:06 +08:00 committed by Craftsman
parent b3283e3d78
commit 5ad2775eb8
8 changed files with 129 additions and 47 deletions

View File

@ -7,6 +7,7 @@ import {
batchArchivedPlanUrl,
batchCopyPlanUrl,
batchDeletePlanUrl,
BatchDisassociateApiCaseUrl,
BatchDisassociateCaseUrl,
BatchEditTestPlanUrl,
batchMovePlanUrl,
@ -17,15 +18,19 @@ import {
deletePlanUrl,
DeleteScheduleTaskUrl,
DeleteTestPlanModuleUrl,
DisassociateApiCaseUrl,
DisassociateCaseUrl,
dragPlanOnGroupUrl,
ExecuteHistoryUrl,
ExecutePlanUrl,
followPlanUrl,
GenerateReportUrl,
GetApiCaseModuleCountUrl,
GetApiCaseModuleUrl,
GetAssociatedBugUrl,
GetFeatureCaseModuleCountUrl,
GetFeatureCaseModuleUrl,
GetPlanDetailApiCaseListUrl,
GetPlanDetailFeatureCaseListUrl,
getStatisticalCountUrl,
GetTestPlanCaseListUrl,
@ -59,6 +64,7 @@ import { DragSortParams, ModuleTreeNode } from '@/models/common';
import type {
AddTestPlanParams,
AssociateCaseRequestType,
BatchApiCaseParams,
BatchExecuteFeatureCaseParams,
BatchFeatureCaseParams,
BatchUpdateCaseExecutorParams,
@ -70,6 +76,8 @@ import type {
FollowPlanParams,
PassRateCountDetail,
PlanDetailApiCaseItem,
PlanDetailApiCaseQueryParams,
PlanDetailApiCaseTreeParams,
PlanDetailApiScenarioItem,
PlanDetailBugItem,
PlanDetailExecuteHistoryItem,
@ -253,9 +261,25 @@ export function testPlanCancelBug(id: string) {
export function executeHistory(data: ExecuteHistoryType) {
return MSR.post<ExecuteHistoryItem[]>({ url: ExecuteHistoryUrl, data });
}
// 计划详情-接口用例列表 TODO 联调
export function getPlanDetailApiCaseList(data: PlanDetailFeatureCaseListQueryParams) {
return MSR.post<CommonList<PlanDetailApiCaseItem>>({ url: GetPlanDetailFeatureCaseListUrl, data });
// 计划详情-接口用例列表
export function getPlanDetailApiCaseList(data: PlanDetailApiCaseQueryParams) {
return MSR.post<CommonList<PlanDetailApiCaseItem>>({ url: GetPlanDetailApiCaseListUrl, data });
}
// 计划详情-接口用例模块树
export function getApiCaseModule(data: PlanDetailApiCaseTreeParams) {
return MSR.post<ModuleTreeNode[]>({ url: GetApiCaseModuleUrl, data });
}
// 计划详情-接口用例-获取模块数量
export function getApiCaseModuleCount(data: PlanDetailApiCaseQueryParams) {
return MSR.post({ url: GetApiCaseModuleCountUrl, data });
}
// 计划详情-接口用例列表-取消关联用例
export function disassociateApiCase(data: DisassociateCaseParams) {
return MSR.post({ url: DisassociateApiCaseUrl, data });
}
// 计划详情-接口用例列表-批量取消关联用例
export function batchDisassociateApiCase(data: BatchApiCaseParams) {
return MSR.post({ url: BatchDisassociateApiCaseUrl, data });
}
// 计划详情-接口场景列表 TODO 联调
export function getPlanDetailApiScenarioList(data: PlanDetailFeatureCaseListQueryParams) {

View File

@ -96,3 +96,13 @@ export const ConfigScheduleUrl = '/test-plan/schedule-config';
export const ExecutePlanUrl = '/test-plan-execute/start';
// 测试计划-删除定时任务
export const DeleteScheduleTaskUrl = 'test-plan/schedule-config-delete';
// 计划详情-接口用例列表
export const GetPlanDetailApiCaseListUrl = '/test-plan/api/case/page';
// 计划详情-接口用例模块树
export const GetApiCaseModuleUrl = '/test-plan/api/case/tree';
// 计划详情-接口用例列表-取消关联用例
export const DisassociateApiCaseUrl = '/test-plan/api/case/disassociate';
// 计划详情-接口用例列表-批量取消关联用例
export const BatchDisassociateApiCaseUrl = '/test-plan/api/case/batch/disassociate';
// 计划详情-接口用例-获取模块数量
export const GetApiCaseModuleCountUrl = '/test-plan/api/case/module/count';

View File

@ -257,24 +257,48 @@ export interface BatchMoveParams extends TableQueryParams {
targetId?: string | number;
}
// TODO: 联调
// 计划详情-接口用例
export interface PlanDetailApiCaseQueryParams extends TableQueryParams, TestPlanBaseParams {
apiDefinitionId?: string;
protocols: string[];
moduleIds?: string[];
versionId?: string;
refId?: string;
collectionId?: string;
}
export interface PlanDetailApiCaseTreeParams {
testPlanId: string;
treeType: 'MODULE' | 'COLLECTION';
}
export interface PlanDetailApiCaseItem {
id: string;
num: string;
num: number;
name: string;
moduleId: string;
versionName: string;
createUser: string;
createUserName: string;
lastExecResult: LastExecuteResults;
lastExecTime: number;
lastExecResultReportId: string;
executeUser: string;
executeUserName: string;
bugCount: number;
customFields: customFieldsItem[]; // 自定义字段集合
caseId: string;
priority: string;
path: string;
projectId: string;
projectName: string;
environmentId: string;
environmentName: string;
testPlanCollectionId: string;
collectEnvironmentId: string;
}
export interface BatchApiCaseParams extends BatchActionQueryParams {
testPlanId: string;
lastExecResultReportId: string;
moduleIds?: string[];
collectionId?: string;
protocols: string[];
}
// TODO: 联调

View File

@ -21,7 +21,11 @@
<a-dsubmenu>
<template #default>{{ t('ms.paramsInput.protocol') }}</template>
<template #content>
<a-checkbox :model-value="isCheckedAll" :indeterminate="indeterminate" @change="handleChangeAll"
<a-checkbox
class="checkbox-all"
:model-value="isCheckedAll"
:indeterminate="indeterminate"
@change="handleChangeAll"
>{{ t('common.all') }}
</a-checkbox>
<a-checkbox-group direction="vertical" :model-value="selectedProtocols" @change="handleGroupChange">
@ -34,7 +38,11 @@
</template>
<!-- 没有 展开请求的开关 -->
<template v-else>
<a-checkbox :model-value="isCheckedAll" :indeterminate="indeterminate" @change="handleChangeAll"
<a-checkbox
class="checkbox-all"
:model-value="isCheckedAll"
:indeterminate="indeterminate"
@change="handleChangeAll"
>{{ t('common.all') }}
</a-checkbox>
<a-checkbox-group direction="vertical" :model-value="selectedProtocols" @change="handleGroupChange">
@ -141,7 +149,17 @@
.arco-dropdown {
padding: 8px;
.arco-dropdown-list .arco-dropdown-option {
margin: 0;
width: 107px;
}
.checkbox-all {
border-bottom: 1px solid var(--color-text-n8);
}
.arco-checkbox {
padding: 6px 12px;
line-height: 24px;
}
.arco-switch {
margin-left: 8px;
}
}
.api-expend {

View File

@ -84,7 +84,7 @@
</template>
<script setup lang="ts">
import { computed, onBeforeMount, ref } from 'vue';
import { computed, ref } from 'vue';
import { Message } from '@arco-design/web-vue';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
@ -105,8 +105,8 @@
import {
associationCaseToPlan,
batchDisassociateCase,
disassociateCase,
batchDisassociateApiCase,
disassociateApiCase,
getPlanDetailApiCaseList,
sortFeatureCase,
} from '@/api/modules/test-plan/testPlan';
@ -118,7 +118,7 @@
import { hasAnyPermission } from '@/utils/permission';
import { DragSortParams, ModuleTreeNode } from '@/models/common';
import type { PlanDetailApiCaseItem, PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan';
import type { PlanDetailApiCaseItem, PlanDetailApiCaseQueryParams } from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
@ -139,10 +139,11 @@
moduleTree: ModuleTreeNode[];
repeatCase: boolean;
canEdit: boolean;
selectedProtocols: string[];
}>();
const emit = defineEmits<{
(e: 'getModuleCount', params: PlanDetailFeatureCaseListQueryParams): void;
(e: 'getModuleCount', params: PlanDetailApiCaseQueryParams): void;
(e: 'refresh'): void;
(e: 'initModules'): void;
}>();
@ -352,6 +353,8 @@
testPlanId: props.planId,
projectId: appStore.currentProjectId,
moduleIds: selectModules,
protocols: props.selectedProtocols,
collectionId: props.activeModule,
};
if (isBatch) {
return {
@ -379,12 +382,9 @@
pageSize: propsRes.value.msPagination?.pageSize,
});
}
watch(
() => props.activeModule,
() => {
watch([() => props.activeModule, () => props.selectedProtocols], () => {
loadCaseList();
}
);
});
async function getModuleCount() {
const tableParams = await getTableParams(false);
@ -400,7 +400,7 @@
const reportId = ref('');
function showReport(record: PlanDetailApiCaseItem) {
reportVisible.value = true;
reportId.value = record.lastExecResultReportId; // TODO
reportId.value = record.lastExecResultReportId;
}
const tableSelected = ref<(string | number)[]>([]); //
@ -439,7 +439,7 @@
try {
// TODO
await associationCaseToPlan({
functionalSelectIds: [record.caseId],
functionalSelectIds: [record.id],
testPlanId: props.planId,
});
Message.success(t('ms.case.associate.associateSuccess'));
@ -456,8 +456,7 @@
async function handleDisassociateCase(record: PlanDetailApiCaseItem, done?: () => void) {
try {
disassociateLoading.value = true;
// TODO
await disassociateCase({ testPlanId: props.planId, id: record.id });
await disassociateApiCase({ testPlanId: props.planId, id: record.id });
if (done) {
done();
}
@ -486,8 +485,7 @@
onBeforeOk: async () => {
try {
const tableParams = await getTableParams(true);
// TODO
await batchDisassociateCase({
await batchDisassociateApiCase({
selectIds: tableSelected.value as string[],
selectAll: batchParams.value.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
@ -525,10 +523,6 @@
}
}
onBeforeMount(() => {
loadCaseList();
});
defineExpose({
resetSelector,
loadCaseList,

View File

@ -15,6 +15,7 @@
:all-count="allCount"
:show-expand-api="false"
@set-active-folder="setActiveFolder"
@selected-protocols-change="selectedProtocolsChange"
/>
<a-divider class="my-[8px]" />
<a-spin class="min-h-[200px] w-full" :loading="loading">
@ -48,7 +49,7 @@
</template>
<script setup lang="ts">
import { computed, onBeforeMount, ref, watch } from 'vue';
import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useVModel } from '@vueuse/core';
@ -56,7 +57,7 @@
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
import TreeFolderAll from '@/views/api-test/components/treeFolderAll.vue';
import { getFeatureCaseModule } from '@/api/modules/test-plan/testPlan';
import { getApiCaseModule } from '@/api/modules/test-plan/testPlan';
import { useI18n } from '@/hooks/useI18n';
import { mapTree } from '@/utils';
@ -65,10 +66,12 @@
const props = defineProps<{
modulesCount?: Record<string, number>; //
selectedKeys: string[]; // key
treeType: 'MODULE' | 'COLLECTION';
}>();
const emit = defineEmits<{
(e: 'folderNodeSelect', ids: string[], _offspringIds: string[], nodeName?: string): void;
(e: 'init', params: ModuleTreeNode[]): void;
(e: 'changeProtocol', selectedProtocols: string[]): void;
}>();
const route = useRoute();
@ -102,8 +105,7 @@
async function initModules() {
try {
loading.value = true;
// TODO
const res = await getFeatureCaseModule(route.query.id as string);
const res = await getApiCaseModule({ testPlanId: route.query.id as string, treeType: props.treeType });
folderTree.value = mapTree<ModuleTreeNode>(res, (node) => {
return {
...node,
@ -130,10 +132,6 @@
emit('folderNodeSelect', _selectedKeys as string[], offspringIds, node.name);
}
onBeforeMount(() => {
initModules();
});
//
watch(
() => props.modulesCount,
@ -148,6 +146,11 @@
}
);
function selectedProtocolsChange() {
emit('changeProtocol', selectedProtocols.value);
initModules();
}
defineExpose({
initModules,
});

View File

@ -5,8 +5,10 @@
ref="caseTreeRef"
:modules-count="modulesCount"
:selected-keys="selectedKeys"
:tree-type="props.treeType"
@folder-node-select="handleFolderNodeSelect"
@init="initModuleTree"
@change-protocol="handleProtocolChange"
/>
</template>
<template #second>
@ -20,6 +22,7 @@
:offspring-ids="offspringIds"
:module-tree="moduleTree"
:can-edit="props.canEdit"
:selected-protocols="selectedProtocols"
@get-module-count="getModuleCount"
@refresh="emit('refresh')"
@init-modules="initModules"
@ -36,14 +39,15 @@
import CaseTable from './components/caseTable.vue';
import CaseTree from './components/caseTree.vue';
import { getFeatureCaseModuleCount } from '@/api/modules/test-plan/testPlan';
import { getApiCaseModuleCount } from '@/api/modules/test-plan/testPlan';
import { ModuleTreeNode } from '@/models/common';
import type { PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan';
import type { PlanDetailApiCaseQueryParams } from '@/models/testPlan/testPlan';
const props = defineProps<{
repeatCase: boolean;
canEdit: boolean;
treeType: 'MODULE' | 'COLLECTION';
}>();
const emit = defineEmits<{
@ -54,10 +58,13 @@
const planId = ref(route.query.id as string);
const modulesCount = ref<Record<string, any>>({});
async function getModuleCount(params: PlanDetailFeatureCaseListQueryParams) {
const selectedProtocols = ref<string[]>([]);
function handleProtocolChange(val: string[]) {
selectedProtocols.value = val;
}
async function getModuleCount(params: PlanDetailApiCaseQueryParams) {
try {
// TODO
modulesCount.value = await getFeatureCaseModuleCount(params);
modulesCount.value = await getApiCaseModuleCount(params);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);

View File

@ -108,9 +108,11 @@
@refresh="initDetail"
/>
<BugManagement v-if="activeTab === 'defectList'" :can-edit="detail.status !== 'ARCHIVED'" @refresh="initDetail" />
<!-- TODO 切换模块视图 -->
<ApiCase
v-if="activeTab === 'apiCase'"
ref="apiCaseRef"
tree-type="MODULE"
:repeat-case="detail.repeatCase"
:can-edit="detail.status !== 'ARCHIVED'"
@refresh="initDetail"