feat(测试计划): 测试计划详情-接口用例执行结果&功能用例移动

This commit is contained in:
teukkk 2024-06-13 16:43:50 +08:00 committed by 刘瑞斌
parent 1c4f4e0fec
commit b17bacd723
14 changed files with 152 additions and 44 deletions

View File

@ -2,6 +2,10 @@ import MSR from '@/api/http/index';
import {
addTestPlanModuleUrl,
AddTestPlanUrl,
ApiCaseReportDetailStepUrl,
ApiCaseReportDetailUrl,
ApiScenarioReportDetailStepUrl,
ApiScenarioReportDetailUrl,
archivedPlanUrl,
associationCaseToPlanUrl,
batchArchivedPlanUrl,
@ -13,6 +17,7 @@ import {
BatchEditTestPlanUrl,
BatchMoveApiCaseUrl,
BatchMoveApiScenarioUrl,
BatchMoveFeatureCaseUrl,
batchMovePlanUrl,
BatchRunApiCaseUrl,
BatchRunApiScenarioUrl,
@ -75,6 +80,7 @@ import {
} from '@/api/requrls/test-plan/testPlan';
import { ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management';
import type { ReportDetail, ReportStepDetail } from '@/models/apiTest/report';
import { ReviewUserItem } from '@/models/caseManagement/caseReview';
import type { CaseManagementTable, CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
import type { CommonList, MoveModules, TableQueryParams } from '@/models/common';
@ -261,6 +267,10 @@ export const GetTestPlanUsers = (projectId: string, keyword: string) => {
export function batchUpdateCaseExecutor(data: BatchUpdateCaseExecutorParams) {
return MSR.post({ url: BatchUpdateCaseExecutorUrl, data });
}
// 计划详情-功能用例列表-批量移动
export function batchMoveFeatureCase(data: BatchMoveApiCaseParams) {
return MSR.post({ url: BatchMoveFeatureCaseUrl, data });
}
// 计划详情-功能用例-执行
export function runFeatureCase(data: RunFeatureCaseParams) {
return MSR.post({ url: RunFeatureCaseUrl, data });
@ -325,6 +335,14 @@ export function batchRunApiCase(data: BatchApiCaseParams) {
export function batchMoveApiCase(data: BatchMoveApiCaseParams) {
return MSR.post({ url: BatchMoveApiCaseUrl, data });
}
// 计划详情-接口用例列表-获取报告
export function getApiCaseReport(reportId: string) {
return MSR.get<ReportDetail>({ url: `${ApiCaseReportDetailUrl}/${reportId}` });
}
// 计划详情-接口用例列表-获取报告-步骤详情
export function getApiCaseReportStep(reportId: string, stepId: string) {
return MSR.get<ReportStepDetail[]>({ url: `${ApiCaseReportDetailStepUrl}/${reportId}/${stepId}` });
}
// 计划详情-接口场景列表
export function getPlanDetailApiScenarioList(data: PlanDetailApiScenarioQueryParams) {
return MSR.post<CommonList<PlanDetailApiScenarioItem>>({ url: GetPlanDetailApiScenarioListUrl, data });
@ -365,6 +383,14 @@ export function batchRunApiScenario(data: BatchApiCaseParams) {
export function batchMoveApiScenario(data: BatchMoveApiCaseParams) {
return MSR.post({ url: BatchMoveApiScenarioUrl, data });
}
// 计划详情-接口场景列表-获取报告
export function getApiScenarioReport(reportId: string) {
return MSR.get<ReportDetail>({ url: `${ApiScenarioReportDetailUrl}/${reportId}` });
}
// 计划详情-接口用例列表-获取报告-步骤详情
export function getApiScenarioReportStep(reportId: string, stepId: string) {
return MSR.get<ReportStepDetail[]>({ url: `${ApiScenarioReportDetailStepUrl}/${reportId}/${stepId}` });
}
// 计划详情-执行历史 TODO 联调
export function getPlanDetailExecuteHistory(data: PlanDetailFeatureCaseListQueryParams) {
return MSR.post<CommonList<PlanDetailExecuteHistoryItem>>({ url: PlanDetailExecuteHistoryUrl, data });

View File

@ -62,6 +62,8 @@ export const DisassociateCaseUrl = '/test-plan/functional/case/disassociate';
export const BatchDisassociateCaseUrl = '/test-plan/functional/case/batch/disassociate';
// 计划详情-功能用例-执行
export const RunFeatureCaseUrl = '/test-plan/functional/case/run';
// 计划详情-功能用例列表-批量移动
export const BatchMoveFeatureCaseUrl = '/test-plan/functional/case/batch/move';
// 测试计划-用例详情-缺陷列表
export const GetAssociatedBugUrl = '/test-plan/functional/case/has/associate/bug/page';
// 测试计划-用例详情
@ -119,6 +121,10 @@ export const BatchUpdateApiCaseExecutorUrl = '/test-plan/api/case/batch/update/e
export const BatchRunApiCaseUrl = '/test-plan/api/case/batch/run';
// 计划详情-接口用例列表-批量移动
export const BatchMoveApiCaseUrl = '/test-plan/api/case/batch/move';
// 计划详情-接口用例列表-报告详情
export const ApiCaseReportDetailUrl = '/test-plan/api/case/report/get';
// 计划详情-接口用例列表-步骤详情
export const ApiCaseReportDetailStepUrl = '/test-plan/api/case/report/get/detail';
// 计划详情-接口场景列表
export const GetPlanDetailApiScenarioListUrl = '/test-plan/api/scenario/page';
@ -140,6 +146,10 @@ export const BatchUpdateApiScenarioExecutorUrl = '/test-plan/api/scenario/batch/
export const BatchRunApiScenarioUrl = '/test-plan/api/scenario/batch/run';
// 计划详情-接口场景列表-批量移动
export const BatchMoveApiScenarioUrl = '/test-plan/api/scenario/batch/move';
// 计划详情-接口场景列表-报告详情
export const ApiScenarioReportDetailUrl = '/test-plan/api/scenario/report/get';
// 计划详情-接口场景列表-步骤详情
export const ApiScenarioReportDetailStepUrl = '/test-plan/api/scenario/report/get/detail';
// 测试规划脑图
export const GetPlanMinderUrl = '/test-plan/mind/data';

View File

@ -257,6 +257,7 @@ export enum ScenarioStepType {
API_CASE = 'API_CASE', // 接口用例
LOOP_CONTROLLER = 'LOOP_CONTROLLER', // 循环控制器
API = 'API', // 接口定义
TEST_PLAN_API_CASE = 'TEST_PLAN_API_CASE', // 测试计划接口用例
CUSTOM_REQUEST = 'CUSTOM_REQUEST', // 自定义请求
API_SCENARIO = 'API_SCENARIO', // 场景
IF_CONTROLLER = 'IF_CONTROLLER', // 条件控制器

View File

@ -28,8 +28,12 @@
</template>
</a-dropdown>
</template>
<CaseReportCom v-if="!props.isScenario" :detail-info="reportStepDetail" />
<ScenarioCom v-else :detail-info="reportStepDetail" />
<CaseReportCom
v-if="!props.isScenario"
:detail-info="reportStepDetail"
:get-report-step-detail="props.getReportStepDetail"
/>
<ScenarioCom v-else :detail-info="reportStepDetail" :get-report-step-detail="props.getReportStepDetail" />
</MsDrawer>
</template>
@ -54,6 +58,8 @@
reportId: string;
isScenario?: boolean;
doNotShowShare?: boolean; //
reportDetail?: (...args: any) => Promise<any>; //
getReportStepDetail?: (...args: any) => Promise<any>; //
}>();
const appStore = useAppStore();
@ -108,6 +114,10 @@
});
async function getReportDetail() {
try {
if (props.reportDetail) {
reportStepDetail.value = await props.reportDetail(props.reportId);
return;
}
if (props.isScenario) {
reportStepDetail.value = await reportScenarioDetail(props.reportId);
} else {

View File

@ -145,6 +145,7 @@
isResponseModel?: boolean;
reportId?: string;
steps?: ScenarioItemType[]; //
getReportStepDetail?: (...args: any) => Promise<any>; //
}>();
const { t } = useI18n();
@ -270,11 +271,16 @@
try {
loading.value = true;
if (props.stepItem) {
const res = await reportDetailMap[props.showType].stepDetail(
(props.stepItem?.reportId || props.reportId) as string,
stepId,
route.query.shareId as string | undefined
);
let res;
if (props.getReportStepDetail) {
res = await props.getReportStepDetail((props.stepItem?.reportId || props.reportId) as string, stepId);
} else {
res = await reportDetailMap[props.showType].stepDetail(
(props.stepItem?.reportId || props.reportId) as string,
stepId,
route.query.shareId as string | undefined
);
}
stepDetailInfo.value = cloneDeep(res) as any;
// TODO --
activeStepDetail.value = stepDetailInfo.value[activeIndex.value];

View File

@ -163,6 +163,7 @@
show-type="CASE"
:active-type="activeTab"
:report-detail="detail || []"
:get-report-step-detail="props.getReportStepDetail"
/>
</div>
<!-- 报告明细结束 -->
@ -171,7 +172,6 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import { cloneDeep } from 'lodash-es';
import SetReportChart from './case/setReportChart.vue';
@ -186,11 +186,10 @@
import { getIndicators } from '../utils';
const route = useRoute();
const { t } = useI18n();
const props = defineProps<{
detailInfo?: ReportDetail;
getReportStepDetail?: (...args: any) => Promise<any>; //
}>();
const detail = ref<ReportDetail>({

View File

@ -36,7 +36,13 @@
<!-- 报告明细开始 -->
<div class="report-info">
<reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" show-type="API" />
<TiledList :key-words="cascaderKeywords" show-type="API" :active-type="activeTab" :report-detail="detail || []" />
<TiledList
:key-words="cascaderKeywords"
show-type="API"
:get-report-step-detail="props.getReportStepDetail"
:active-type="activeTab"
:report-detail="detail || []"
/>
</div>
<!-- 报告明细结束 -->
</div>
@ -63,6 +69,7 @@
const { t } = useI18n();
const props = defineProps<{
detailInfo?: ReportDetail;
getReportStepDetail?: (...args: any) => Promise<any>; //
}>();
const detail = ref<ReportDetail>({

View File

@ -23,6 +23,7 @@
:step-item="props.scenarioDetail"
:console="props.console"
:is-definition="true"
:get-report-step-detail="props.getReportStepDetail"
:report-id="props.scenarioDetail?.reportId"
/>
</div>
@ -46,6 +47,7 @@
scenarioDetail?: ScenarioItemType;
showType: 'API' | 'CASE'; // |
console?: string; //
getReportStepDetail?: (...args: any) => Promise<any>; //
}>();
const emit = defineEmits<{

View File

@ -240,6 +240,7 @@
ScenarioStepType.API_CASE,
ScenarioStepType.CUSTOM_REQUEST,
ScenarioStepType.SCRIPT,
ScenarioStepType.TEST_PLAN_API_CASE,
]);
const innerNumber = ref<number>(0);
@ -296,6 +297,7 @@
ScenarioStepType.LOOP_CONTROLLER,
ScenarioStepType.IF_CONTROLLER,
ScenarioStepType.ONCE_ONLY_CONTROLLER,
ScenarioStepType.TEST_PLAN_API_CASE,
]);
function getShowExpand(item: ScenarioItemType) {
if (props.showType === 'API') {

View File

@ -26,6 +26,7 @@
:show-type="props.showType"
:console="props.reportDetail.console"
:report-id="props.reportDetail.id"
:get-report-step-detail="props.getReportStepDetail"
/>
</div>
</template>
@ -47,6 +48,7 @@
activeType: 'tiled' | 'tab'; // |tab
showType: 'API' | 'CASE'; // |
keyWords: string;
getReportStepDetail?: (...args: any) => Promise<any>; //
}>();
const tiledList = ref<ScenarioItemType[]>([]);

View File

@ -39,6 +39,7 @@
<ExecuteResult :execute-result="record.lastExecResult" />
<MsIcon
v-show="record.lastExecResult !== LastExecuteResults.PENDING"
v-permission="['PROJECT_TEST_PLAN:READ']"
type="icon-icon_take-action_outlined"
class="ml-[8px] cursor-pointer text-[rgb(var(--primary-5))]"
size="16"
@ -72,7 +73,13 @@
</MsPopconfirm>
</template>
</MsBaseTable>
<CaseAndScenarioReportDrawer v-model:visible="reportVisible" :report-id="reportId" do-not-show-share />
<CaseAndScenarioReportDrawer
v-model:visible="reportVisible"
:report-id="reportId"
do-not-show-share
:report-detail="getApiCaseReport"
:get-report-step-detail="getApiCaseReportStep"
/>
<!-- 批量修改执行人 -->
<BatchUpdateExecutorModal
v-model:visible="batchUpdateExecutorModalVisible"
@ -122,6 +129,8 @@
batchRunApiCase,
batchUpdateApiCaseExecutor,
disassociateApiCase,
getApiCaseReport,
getApiCaseReportStep,
getPlanDetailApiCaseList,
runApiCase,
sortApiCase,

View File

@ -39,6 +39,7 @@
<ExecuteResult :execute-result="record.lastExecResult" />
<MsIcon
v-show="record.lastExecResult !== LastExecuteResults.PENDING"
v-permission="['PROJECT_TEST_PLAN:READ']"
type="icon-icon_take-action_outlined"
class="ml-[8px] cursor-pointer text-[rgb(var(--primary-5))]"
size="16"
@ -49,7 +50,12 @@
<apiStatus :status="record.status" />
</template>
<template v-if="props.canEdit" #operation="{ record }">
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+EXECUTE']" type="text" class="!mr-0" @click="handleRun">
<MsButton
v-permission="['PROJECT_TEST_PLAN:READ+EXECUTE']"
type="text"
class="!mr-0"
@click="handleRun(record)"
>
{{ t('common.execute') }}
</MsButton>
<a-divider v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']" direction="vertical" :margin="8"></a-divider>
@ -67,7 +73,14 @@
</MsPopconfirm>
</template>
</MsBaseTable>
<CaseAndScenarioReportDrawer v-model:visible="reportVisible" :report-id="reportId" do-not-show-share />
<CaseAndScenarioReportDrawer
v-model:visible="reportVisible"
:report-id="reportId"
do-not-show-share
is-scenario
:report-detail="getApiScenarioReport"
:get-report-step-detail="getApiScenarioReportStep"
/>
<!-- 批量修改执行人 -->
<BatchUpdateExecutorModal
v-model:visible="batchUpdateExecutorModalVisible"
@ -116,6 +129,8 @@
batchRunApiScenario,
batchUpdateApiScenarioExecutor,
disassociateApiScenario,
getApiScenarioReport,
getApiScenarioReportStep,
getPlanDetailApiScenarioList,
runApiScenario,
sortApiScenario,

View File

@ -108,10 +108,19 @@
<BatchUpdateExecutorModal
v-model:visible="batchUpdateExecutorModalVisible"
:count="batchParams.currentSelectCount || tableSelected.length"
:params="batchUpdateExecutorParams"
:params="batchUpdateParams"
:batch-update-executor="batchUpdateCaseExecutor"
@load-list="resetSelectorAndCaseList"
/>
<!-- 批量移动 -->
<BatchApiMoveModal
v-model:visible="batchMoveModalVisible"
:module-tree="props.moduleTree"
:count="batchParams.currentSelectCount || tableSelected.length"
:params="batchUpdateParams"
:batch-move="batchMoveFeatureCase"
@load-list="resetCaseList"
/>
</div>
</template>
@ -134,12 +143,14 @@
import CaseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
import BugCountPopover from './bugCountPopover.vue';
import BatchApiMoveModal from '@/views/test-plan/testPlan/components/batchApiMoveModal.vue';
import BatchUpdateExecutorModal from '@/views/test-plan/testPlan/components/batchUpdateExecutorModal.vue';
import ExecuteForm from '@/views/test-plan/testPlan/detail/featureCase/components/executeForm.vue';
import {
batchDisassociateCase,
batchExecuteCase,
batchMoveFeatureCase,
batchUpdateCaseExecutor,
disassociateCase,
getPlanDetailFeatureCaseList,
@ -322,27 +333,30 @@
}
);
const batchActions = {
baseAction: [
{
label: 'common.execute',
eventTag: 'execute',
permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'],
},
{
label: 'testPlan.featureCase.changeExecutor',
eventTag: 'changeExecutor',
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
},
],
moreAction: [
{
label: 'common.cancelLink',
eventTag: 'disassociate',
permission: ['PROJECT_TEST_PLAN:READ+ASSOCIATION'],
},
],
};
const batchActions = computed(() => {
return {
baseAction: [
{
label: 'common.execute',
eventTag: 'execute',
permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'],
},
{
label: 'testPlan.featureCase.changeExecutor',
eventTag: 'changeExecutor',
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
},
...(props.treeType === 'COLLECTION'
? [{ label: 'common.move', eventTag: 'move', permission: ['PROJECT_TEST_PLAN:READ+UPDATE'] }]
: []),
{
label: 'common.cancelLink',
eventTag: 'disassociate',
permission: ['PROJECT_TEST_PLAN:READ+ASSOCIATION'],
},
],
};
});
const tableRef = ref<InstanceType<typeof MsBaseTable>>();
watch(
@ -566,15 +580,22 @@
batchExecuteForm.value = { ...defaultExecuteForm };
}
//
//
const batchUpdateParams = ref();
const batchUpdateExecutorModalVisible = ref(false);
const batchUpdateExecutorParams = ref();
const batchMoveModalVisible = ref(false);
//
async function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
tableSelected.value = params?.selectedIds || [];
batchParams.value = { ...params, selectIds: params?.selectedIds };
const tableParams = await getTableParams(true);
batchUpdateParams.value = {
selectIds: tableSelected.value as string[],
selectAll: batchParams.value.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
...tableParams,
};
switch (event.eventTag) {
case 'execute':
batchExecuteModalVisible.value = true;
@ -583,14 +604,11 @@
handleBatchDisassociateCase();
break;
case 'changeExecutor':
batchUpdateExecutorParams.value = {
selectIds: tableSelected.value as string[],
selectAll: batchParams.value.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
...tableParams,
};
batchUpdateExecutorModalVisible.value = true;
break;
case 'move':
batchMoveModalVisible.value = true;
break;
default:
break;
}

View File

@ -112,6 +112,7 @@ export default {
'testPlan.featureCase.richTextDblclickPlaceholder': 'Double click for quick input',
'testPlan.featureCase.autoNextTip1': 'Enable: After submitting the results, jump to the next case',
'testPlan.featureCase.autoNextTip2': 'Close: After submitting the results, it is still in the current state',
'testPlan.api.testSetRequired': 'The test set cannot be empty',
'testPlan.executeHistory.executionStartAndEndTime': 'Execution start and end time',
'testPlan.testPlanGroup.seeArchived': 'Only see archived',
'testPlan.testPlanGroup.planNamePlaceholder': 'Please enter the name of the test plan group',