fix(测试计划): 修复测试计划报告bug&报告明细展示问题&状态等问题

This commit is contained in:
xinxin.wu 2024-06-17 19:50:33 +08:00 committed by Craftsman
parent ae5ea6394e
commit 4b14f649a9
10 changed files with 119 additions and 39 deletions

View File

@ -579,11 +579,20 @@
function writeAssociateCases(param: AssociateCaseRequest) {
selectedAssociateCasesParams.value = { ...param };
const node: PlanMinderNode = window.minder.getSelectedNode();
let associateType: string = '';
// TODO node
if (node.data.type === PlanMinderCollectionType.SCENARIO) {
associateType = PlanMinderAssociateType.SCENARIO_CASE;
} else {
associateType =
node.data.type === PlanMinderCollectionType.API && param.associateApiType
? param.associateApiType
: node.data.type;
}
node.data.associateDTOS = [
{
ids: param.selectIds,
associateType:
node.data.type === PlanMinderCollectionType.SCENARIO ? PlanMinderAssociateType.SCENARIO_CASE : node.data.type,
associateType,
},
];
caseAssociateVisible.value = false;

View File

@ -37,6 +37,7 @@ export interface AssociateCaseRequest extends BatchApiParams {
apiScenarioSelectIds?: string[];
totalCount?: number;
testPlanId?: string;
associateApiType?: string;
}
export type AssociateCaseRequestType = Pick<AssociateCaseRequest, 'functionalSelectIds' | 'testPlanId'>;

View File

@ -4,7 +4,7 @@
size="160px"
:legend-data="legendData"
:options="executeCharOptions"
:request-total="getIndicators(detail.caseTotal) || 0"
:request-total="getIndicators(getTotal) || 0"
/>
</template>
@ -97,6 +97,12 @@
}) as unknown as LegendData[];
}
const getTotal = computed(() => {
const { executeCount } = props.detail;
const { success, error, fakeError, pending, block } = executeCount;
return success + error + fakeError + pending + block;
});
watchEffect(() => {
if (props.detail) {
initExecuteOptions();

View File

@ -21,7 +21,7 @@
<MsButton class="!mx-0" @click="openReport(record)">{{ t('report.detail.testPlanGroup.viewReport') }}</MsButton>
</template>
</MsBaseTable>
<ReportDrawer v-model:visible="reportVisible" :report-id="reportId" />
<ReportDrawer v-model:visible="reportVisible" :report-id="independentReportId" />
</template>
<script setup lang="ts">
@ -133,12 +133,15 @@
const reportVisible = ref(false);
const independentReportId = ref<string>('');
function openReport(record: PlanReportDetail) {
independentReportId.value = record.id;
if (props.currentMode === 'drawer') {
reportVisible.value = true;
} else {
openNewPage(RouteEnum.TEST_PLAN_REPORT_DETAIL, {
reportId: record.id,
id: record.id,
});
}
}

View File

@ -48,7 +48,7 @@
import { useI18n } from '@/hooks/useI18n';
import { characterLimit } from '@/utils';
import type { TestPlanDetail, TestPlanItem } from '@/models/testPlan/testPlan';
import type { CreateTask, TestPlanDetail, TestPlanItem } from '@/models/testPlan/testPlan';
import { testPlanTypeEnum } from '@/enums/testPlanEnum';
const { t } = useI18n();
@ -56,6 +56,7 @@
const props = defineProps<{
visible: boolean;
record: TestPlanItem | TestPlanDetail | undefined; // record
scheduleConfig?: CreateTask;
}>();
const emit = defineEmits<{
@ -98,7 +99,9 @@
case 'ARCHIVED':
return t('testPlan.testPlanIndex.deleteArchivedPlan');
case 'UNDERWAY':
return t('testPlan.testPlanIndex.deleteRunningPlan');
return props.scheduleConfig && props.scheduleConfig.enable
? t('testPlan.testPlanIndex.deleteRunningSchedulePlan')
: t('testPlan.testPlanIndex.deleteRunningPlan');
case 'COMPLETED':
return t('testPlan.testPlanIndex.deleteCompletedPlan');
default:

View File

@ -47,6 +47,7 @@
@batch-action="handleTableBatch"
@filter-change="filterChange"
@drag-change="handleDragChange"
@sorter-change="saveSort"
>
<!-- TODO: 快捷创建暂时不上 -->
<!-- <template v-if="hasAnyPermission(['PROJECT_TEST_PLAN:READ+ADD'])" #quickCreate>
@ -159,30 +160,32 @@
</template>
<template #planStartToEndTime="{ record }">
<div>
{{ record.plannedStartTime ? dayjs(record.plannedStartTime) : '-' }} {{ t('common.to') }}
{{ record.plannedStartTime ? dayjs(record.plannedStartTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
{{ t('common.to') }}
<a-tooltip
class="ms-tooltip-red"
:content="t('testPlan.planStartToEndTimeTip')"
:disabled="record.execStatus !== LastExecuteResults.ERROR"
>
<span :class="[`${record.execStatus === LastExecuteResults.ERROR ? 'text-[rgb(var(--danger-6))' : ''}`]">
{{ record?.plannedEndTime ? dayjs(record.plannedEndTime) : '-' }}
{{ record?.plannedEndTime ? dayjs(record.plannedEndTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
</span>
</a-tooltip>
</div>
</template>
<template #actualStartToEndTime="{ record }">
{{ record?.actualStartTime ? dayjs(record.actualStartTime) : '-' }}{{ t('common.to')
}}{{ record.actualEndTime ? dayjs(record.actualEndTime) : '-' }}
{{ record?.actualStartTime ? dayjs(record.actualStartTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
{{ t('common.to') }} {{ record.actualEndTime ? dayjs(record.actualEndTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
</template>
<template #passRate="{ record }">
<div class="mr-[8px] w-[100px]">
<div v-if="record.type === testPlanTypeEnum.TEST_PLAN" class="mr-[8px] w-[100px]">
<StatusProgress :status-detail="defaultCountDetailMap[record.id]" height="5px" />
</div>
<div class="text-[var(--color-text-1)]">
<div v-if="record.type === testPlanTypeEnum.TEST_PLAN" class="text-[var(--color-text-1)]">
{{ `${defaultCountDetailMap[record.id]?.passRate ? defaultCountDetailMap[record.id].passRate : '-'}%` }}
</div>
<span v-else> - </span>
</template>
<template #passRateTitleSlot="{ columnConfig }">
<div class="flex items-center text-[var(--color-text-3)]">
@ -196,8 +199,13 @@
</div>
</template>
<template #functionalCaseCount="{ record }">
<a-popover position="bottom" content-class="p-[16px]" :disabled="getFunctionalCount(record.id) < 1">
<div>{{ getFunctionalCount(record.id) }}</div>
<a-popover
v-if="record.type === testPlanTypeEnum.TEST_PLAN"
position="bottom"
content-class="p-[16px]"
:disabled="getFunctionalCount(record.id) < 1"
>
<div v-if="record.type === testPlanTypeEnum.TEST_PLAN">{{ getFunctionalCount(record.id) }}</div>
<template #content>
<table class="min-w-[140px] max-w-[176px]">
<tr>
@ -235,14 +243,22 @@
</table>
</template>
</a-popover>
<span v-else>-</span>
</template>
<template #operation="{ record }">
<div class="flex items-center">
<MsButton v-if="isShowExecuteButton(record)" class="!mx-0" @click="executePlan(record)">{{
t('testPlan.testPlanIndex.execution')
}}</MsButton>
<a-divider v-if="isShowExecuteButton(record)" direction="vertical" :margin="8"></a-divider>
<MsButton
v-if="isShowExecuteButton(record, ['PROJECT_TEST_PLAN:READ+EXECUTE'])"
class="!mx-0"
@click="executePlan(record)"
>{{ t('testPlan.testPlanIndex.execution') }}</MsButton
>
<a-divider
v-if="isShowExecuteButton(record, ['PROJECT_TEST_PLAN:READ+EXECUTE'])"
direction="vertical"
:margin="8"
></a-divider>
<MsButton
v-if="hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']) && record.status !== 'ARCHIVED'"
@ -256,10 +272,17 @@
:margin="8"
></a-divider>
<MsButton v-if="!isShowExecuteButton(record)" class="!mx-0" @click="copyTestPlanOrGroup(record.id)">{{
t('common.copy')
}}</MsButton>
<a-divider v-if="!isShowExecuteButton(record)" direction="vertical" :margin="8"></a-divider>
<MsButton
v-if="!isShowExecuteButton(record, ['PROJECT_TEST_PLAN:READ+ADD'])"
class="!mx-0"
@click="copyTestPlanOrGroup(record.id)"
>{{ t('common.copy') }}</MsButton
>
<a-divider
v-if="!isShowExecuteButton(record, ['PROJECT_TEST_PLAN:READ+ADD'])"
direction="vertical"
:margin="8"
></a-divider>
<MsTableMoreAction :list="getMoreActions(record)" @select="handleMoreActionSelect($event, record)" />
</div>
</template>
@ -308,7 +331,12 @@
:task-config="taskForm"
@handle-success="fetchData()"
/>
<ActionModal v-model:visible="showStatusDeleteModal" :record="activeRecord" @success="fetchData()" />
<ActionModal
v-model:visible="showStatusDeleteModal"
:record="activeRecord"
:schedule-config="defaultCountDetailMap[activeRecord?.id || 'none']?.scheduleConfig"
@success="fetchData()"
/>
<BatchEditModal
v-model:visible="showEditModel"
:batch-params="batchParams"
@ -501,6 +529,7 @@
dataIndex: 'moduleId',
showInTable: true,
showDrag: true,
showTooltip: true,
width: 200,
},
{
@ -514,6 +543,7 @@
},
width: 200,
showDrag: true,
showTooltip: true,
},
{
title: 'testPlan.testPlanIndex.planStartToEndTime',
@ -523,7 +553,8 @@
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 200,
width: 370,
showTooltip: true,
showDrag: true,
},
{
@ -534,7 +565,8 @@
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 200,
showTooltip: true,
width: 370,
showDrag: true,
},
{
@ -668,19 +700,19 @@
{
label: 'testPlan.testPlanIndex.createScheduledTask',
eventTag: 'createScheduledTask',
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'],
},
];
const updateAndDeleteScheduledActions: ActionsItem[] = [
{
label: 'testPlan.testPlanIndex.updateScheduledTask',
eventTag: 'updateScheduledTask',
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'],
},
{
label: 'testPlan.testPlanIndex.deleteScheduledTask',
eventTag: 'deleteScheduledTask',
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'],
},
];
@ -695,11 +727,11 @@
return defaultCountDetailMap.value[id].scheduleConfig.enable;
}
function isShowExecuteButton(record: TestPlanItem) {
function isShowExecuteButton(record: TestPlanItem, permission: string[]) {
return (
((record.type === testPlanTypeEnum.TEST_PLAN && getFunctionalCount(record.id) > 0) ||
(record.type === testPlanTypeEnum.GROUP && record.childrenCount)) &&
hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE']) &&
hasAnyPermission(permission) &&
record.status !== 'ARCHIVED'
);
}
@ -708,7 +740,7 @@
const { status: planStatus } = record;
//
const copyAction = isShowExecuteButton(record) ? copyActions : [];
const copyAction = isShowExecuteButton(record, ['PROJECT_TEST_PLAN:READ+ADD']) ? copyActions : [];
let scheduledTaskAction: ActionsItem[] = [];
if (planStatus !== 'ARCHIVED' && record.groupId && record.groupId === 'NONE') {
@ -825,6 +857,11 @@
setLoadListParams(await initTableParams());
loadList();
}
//
const sort = ref<{ [key: string]: string }>({});
function saveSort(sortObj: { [key: string]: string }) {
sort.value = sortObj;
}
//
async function emitTableParams(isInit = false) {
@ -871,7 +908,7 @@
projectId: appStore.currentProjectId,
executeIds: [],
runMode: 'SERIAL',
executionSource: 'JENKINS',
executionSource: 'MANUAL',
};
const executeForm = ref<BatchExecutePlan>(cloneDeep(initExecuteForm));
@ -913,7 +950,7 @@
const params: ExecutePlan = {
executeId: id,
runMode: 'SERIAL',
executionSource: 'JENKINS',
executionSource: 'MANUAL',
};
await executeSinglePlan(params);
Message.success(t('case.detail.execute.success'));
@ -1331,7 +1368,8 @@
() => showType.value,
(val) => {
if (val) {
tableProps.value.draggableCondition = hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']) && val === 'ALL';
tableProps.value.draggableCondition =
hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']) && val !== 'TEST_PLAN' && !Object.keys(sort.value).length;
setPagination({
current: 1,
});
@ -1342,6 +1380,21 @@
}
);
watch(
() => sort.value,
(val) => {
if (val) {
tableProps.value.draggableCondition =
hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']) &&
showType.value !== 'GROUP' &&
!Object.keys(sort.value).length;
}
},
{
deep: true,
}
);
watch(
() => props.activeFolder,
(val) => {

View File

@ -32,8 +32,7 @@
{{ detailCount.errorCount }}
</td>
</tr>
<!-- TODO 这个版本暂时不上 -->
<!-- <tr>
<tr>
<td class="popover-label-td">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--warning-6))]"></div>
<div>{{ t('common.fakeError') }}</div>
@ -41,7 +40,7 @@
<td class="popover-value-td">
{{ detailCount.fakeErrorCount }}
</td>
</tr> -->
</tr>
<tr>
<td class="popover-label-td">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[var(--color-fill-p-3)]"></div>

View File

@ -73,12 +73,15 @@ export default {
'The plan is not executed, the data cannot be recovered after deletion, please operate carefully!',
'testPlan.testPlanIndex.deleteRunningPlan':
'Scheduled tasks are stopped and deleted. Exercise caution when performing this operation',
'testPlan.testPlanIndex.deleteRunningSchedulePlan':
'Scheduled tasks are stopped and deleted. Exercise caution when performing this operation.',
'testPlan.testPlanIndex.deleteCompletedPlan':
'The proposed plan is completed, the option is archived, and the use case information and execution results are retained;If you continue to delete, the data will not be restored, please be careful!',
'testPlan.testPlanIndex.confirmArchivePlan':
'After filing, implement information no longer update and editing, data unrecoverable, please careful operation',
'testPlan.testPlanIndex.passRateTitleTip': 'Passed use cases/all use cases *100%',
'testPlan.planForm.namePlaceholder': 'Please enter the name of the test plan',
'testPlan.planForm.nameGroupPlaceholder': 'Please enter a plan group name',
'testPlan.planForm.nameRequired': 'Test plan name cannot be empty',
'testPlan.planForm.testPlanGroupRequired': 'Plan group cannot be empty',
'testPlan.planForm.planStartAndEndTime': 'Planned start and end time',

View File

@ -68,12 +68,15 @@ export default {
'testPlan.testPlanIndex.deleteArchivedPlan': '计划 已归档,删除后数据不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.deletePendingPlan': '计划 未执行,删除后数据不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.deleteRunningPlan': '计划 进行中,删除后,终止执行且不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.deleteRunningSchedulePlan':
'计划 进行中,删除后,终止执行且不可恢复,定时任务停止并删除,请谨慎操作!',
'testPlan.testPlanIndex.deleteCompletedPlan':
'建议计划 已完成 ,选择归档,用例信息及执行结果都将被保留;若继续删除,数据将不会恢复,请谨慎操作!',
'testPlan.testPlanIndex.confirmArchivePlan': '归档后,执行信息不再更新且不可编辑,数据不可恢复,请谨慎操作!',
'testPlan.testPlanIndex.passRateTitleTip': '已通过用例/全部用例*100%',
'testPlan.testPlanIndex.batchEdit': '批量编辑 (已选 { number } 项数据)',
'testPlan.planForm.namePlaceholder': '请输入测试计划名称',
'testPlan.planForm.nameGroupPlaceholder': '请输入计划组名称',
'testPlan.planForm.nameRequired': '测试计划名称不能为空',
'testPlan.planForm.testPlanGroupRequired': '计划组不能为空',
'testPlan.planForm.planStartAndEndTime': '计划起止时间',

View File

@ -15,7 +15,7 @@
<a-form-item
field="name"
:label="t('testPlan.testPlanGroup.name')"
:rules="[{ required: true, message: t('apiTestDebug.requestNameRequired') }]"
:rules="[{ required: true, message: t('testPlan.planForm.nameGroupPlaceholder') }]"
asterisk-position="end"
>
<a-input