fix(测试计划): 修复测试计划报告bug&测试计划bug&用例文件更新报错

This commit is contained in:
xinxin.wu 2024-06-21 19:09:34 +08:00 committed by Craftsman
parent fa4539be74
commit 8dbb98b414
19 changed files with 168 additions and 128 deletions

View File

@ -188,6 +188,7 @@
width: 24,
slotName: SpecialColumnEnum.ACTION,
fixed: 'right',
cellClass: 'operator-class',
},
];
@ -340,7 +341,9 @@
</script>
<style lang="less" scoped>
:deep(.arco-table-cell-align-left) {
padding: 0 8px !important;
:deep(.operator-class) {
.arco-table-cell-align-left {
padding: 0 8px !important;
}
}
</style>

View File

@ -173,6 +173,7 @@
width: 24,
slotName: SpecialColumnEnum.ACTION,
fixed: 'right',
cellClass: 'operator-class',
},
];
@ -309,7 +310,9 @@
</script>
<style lang="less" scoped>
:deep(.arco-table-cell-align-left) {
padding: 0 8px !important;
:deep(.operator-class) {
.arco-table-cell-align-left {
padding: 0 8px !important;
}
}
</style>

View File

@ -122,7 +122,6 @@
fixed: 'left',
width: 150,
showTooltip: true,
columnSelectorDisabled: true,
},
{
title: 'case.caseName',
@ -175,7 +174,7 @@
slotName: 'createUserName',
dataIndex: 'createUserName',
showTooltip: true,
width: 200,
width: 150,
showDrag: true,
},
{
@ -195,6 +194,7 @@
width: 24,
slotName: SpecialColumnEnum.ACTION,
fixed: 'right',
cellClass: 'operator-class',
},
];
@ -210,40 +210,32 @@
return undefined;
}
const {
propsRes,
propsEvent,
loadList,
setLoadListParams,
resetSelector,
setPagination,
resetFilterParams,
setTableSelected,
} = useTable(
getPageList.value,
{
tableKey: TableKeyEnum.ASSOCIATE_CASE,
showSetting: true,
isSimpleSetting: true,
onlyPageSize: true,
selectable: true,
showSelectAll: true,
heightUsed: 310,
showSelectorAll: false,
},
(record) => {
return {
...record,
caseLevel: getCaseLevel(record),
tags: (record.tags || []).map((item: string, i: number) => {
return {
id: `${record.id}-${i}`,
name: item,
};
}),
};
}
);
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, resetFilterParams, setTableSelected } =
useTable(
getPageList.value,
{
tableKey: TableKeyEnum.ASSOCIATE_CASE,
showSetting: true,
isSimpleSetting: true,
onlyPageSize: true,
selectable: true,
showSelectAll: true,
heightUsed: 310,
showSelectorAll: false,
},
(record) => {
return {
...record,
caseLevel: getCaseLevel(record),
tags: (record.tags || []).map((item: string, i: number) => {
return {
id: `${record.id}-${i}`,
name: item,
};
}),
};
}
);
async function getTableParams() {
const { excludeKeys } = propsRes.value;
@ -333,7 +325,9 @@
</script>
<style lang="less" scoped>
:deep(.arco-table-cell-align-left) {
padding: 0 8px !important;
:deep(.operator-class) {
.arco-table-cell-align-left {
padding: 0 8px !important;
}
}
</style>

View File

@ -156,16 +156,19 @@
title: 'apiScenario.table.columns.passRate',
dataIndex: 'requestPassRate',
showDrag: true,
showInTable: false,
width: 100,
},
{
title: 'apiScenario.table.columns.tags',
dataIndex: 'tags',
isTag: true,
isStringTag: true,
showDrag: true,
},
{
title: 'apiScenario.table.columns.createUser',
dataIndex: 'createUser',
slotName: 'createUserName',
showInTable: false,
showTooltip: true,
showDrag: true,
width: 109,
filterConfig: {
mode: 'remote',
@ -176,43 +179,29 @@
placeholderText: t('caseManagement.featureCase.PleaseSelect'),
},
},
{
title: 'apiScenario.table.columns.tags',
dataIndex: 'tags',
isTag: true,
isStringTag: true,
showDrag: true,
},
{
title: '',
dataIndex: 'action',
width: 24,
slotName: SpecialColumnEnum.ACTION,
fixed: 'right',
cellClass: 'operator-class',
},
];
const getPageList = computed(() => {
return getPublicLinkCaseListMap[props.getPageApiType][props.activeSourceType];
});
const {
propsRes,
propsEvent,
loadList,
setLoadListParams,
resetSelector,
setPagination,
resetFilterParams,
setTableSelected,
} = useTable(getPageList.value, {
tableKey: TableKeyEnum.ASSOCIATE_CASE_API_SCENARIO,
showSetting: true,
isSimpleSetting: true,
onlyPageSize: true,
selectable: true,
showSelectAll: true,
heightUsed: 310,
showSelectorAll: false,
});
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, resetFilterParams, setTableSelected } =
useTable(getPageList.value, {
tableKey: TableKeyEnum.ASSOCIATE_CASE_API_SCENARIO,
showSetting: true,
isSimpleSetting: true,
onlyPageSize: true,
selectable: true,
showSelectAll: true,
heightUsed: 310,
showSelectorAll: false,
});
async function getTableParams() {
const { excludeKeys } = propsRes.value;
@ -302,7 +291,9 @@
</script>
<style lang="less" scoped>
:deep(.arco-table-cell-align-left) {
padding: 0 8px !important;
:deep(.operator-class) {
.arco-table-cell-align-left {
padding: 0 8px !important;
}
}
</style>

View File

@ -323,6 +323,7 @@
try {
await updateFile(appStore.currentProjectId, item.associationId);
Message.success(t('common.updateSuccess'));
emit('uploadSuccess');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);

View File

@ -408,6 +408,10 @@ export default function useTableProps<T>(
filterItem.value = { [`custom_${multiple ? 'multiple' : 'single'}_${dataIndex}`]: filteredValues };
} else {
filterItem.value = { ...getTableQueryParams().filter, [dataIndex]: filteredValues };
loadListParams.value.filter = {
...loadListParams.value.filter,
[dataIndex]: filteredValues,
};
}
propsRes.value.filter = cloneDeep(filterItem.value);
setTableDraggable((filterItem.value[dataIndex] || []).length === 0);

View File

@ -90,6 +90,7 @@ export const defaultReportDetail: PlanReportDetail = {
functionalBugCount: 0, // 用例明细bug总数
apiBugCount: 0, // 接口用例明细bug总数
scenarioBugCount: 0, // 场景用例明细bug总数
testPlanName: '',
};
export const statusConfig: StatusListType[] = [

View File

@ -27,6 +27,7 @@ export interface PlanReportDetail {
functionalBugCount: number; // 用例明细bug总数
apiBugCount: number; // 接口用例明细bug总数
scenarioBugCount: number; // 场景用例明细bug总数
testPlanName: string;
}
export type AnalysisType = 'FUNCTIONAL' | 'API' | 'SCENARIO';

View File

@ -33,12 +33,9 @@
@filter-change="filterChange"
>
<template #name="{ record, rowIndex }">
<div
type="text"
class="one-line-text flex w-full text-[rgb(var(--primary-5))]"
@click="showReportDetail(record.id, rowIndex)"
>{{ characterLimit(record.name) }}</div
>
<div class="one-line-text text-[rgb(var(--primary-5))]" @click="showReportDetail(record.id, rowIndex)">{{
record.name
}}</div>
</template>
<!-- 报告类型 -->
<template #integrated="{ record }">

View File

@ -63,7 +63,9 @@
@change="() => handleStatusChange(record)"
>
<template #label>
<span class="text-[var(--color-text-2)]"> <caseLevel :case-level="record.caseLevel" /></span>
<span class="text-[var(--color-text-2)]">
<caseLevel :case-level="record.caseLevel" />
</span>
</template>
<a-option v-for="item of caseLevelList" :key="item.value" :value="item.value">
<caseLevel :case-level="item.text" />
@ -462,20 +464,19 @@
const firstStaticColumn: MsTableColumn = [
{
'title': 'caseManagement.featureCase.tableColumnID',
'slotName': 'num',
'title': 'ID',
'dataIndex': 'num',
'width': 130,
'showInTable': true,
'slotName': 'num',
'sortIndex': 1,
'sortable': {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
'filter-icon-align-left': true,
'fixed': 'left',
'width': 150,
'showTooltip': true,
'ellipsis': true,
'showDrag': false,
'columnSelectorDisabled': true,
'filter-icon-align-left': true,
},
{
title: 'caseManagement.featureCase.tableColumnName',

View File

@ -702,6 +702,7 @@
try {
await updateFile(currentProjectId.value, item.associationId);
Message.success(t('common.updateSuccess'));
emit('updateSuccess');
} catch (error) {
console.log(error);
}

View File

@ -99,7 +99,7 @@ export function convertToFile(fileInfo: AssociatedList): MsFileItem {
type: `application/${type}`,
});
Object.defineProperty(file, 'size', { value: fileInfo.size });
const { id, createUserName, createTime, local, isUpdateFlag, associateId } = fileInfo;
const { id, createUserName, createTime, local, isUpdateFlag, associationId } = fileInfo;
return {
enable: fileInfo.enable || false,
file,
@ -114,7 +114,7 @@ export function convertToFile(fileInfo: AssociatedList): MsFileItem {
local: !!local,
deleteContent: local ? '' : 'caseManagement.featureCase.cancelLink',
isUpdateFlag,
associateId,
associationId,
};
}

View File

@ -33,12 +33,8 @@
@filter-change="filterChange"
>
<template #name="{ record }">
<div
type="text"
class="one-line-text flex w-full text-[rgb(var(--primary-5))]"
@click="showReportDetail(record.id, record.integrated)"
>
{{ characterLimit(record.name) }}
<div class="one-line-text text-[rgb(var(--primary-5))]" @click="showReportDetail(record.id, record.integrated)">
{{ record.name }}
</div>
</template>
<template #integrated="{ record }">

View File

@ -1,6 +1,6 @@
<template>
<ReportHeader v-if="!props.isDrawer" :detail="detail" :share-id="shareId" :is-group="false" />
<div class="analysis-wrapper">
<div class="analysis-wrapper" :data-cards="cardCount">
<div class="analysis min-w-[238px]">
<div class="block-title">{{ t('report.detail.api.reportAnalysis') }}</div>
<ReportMetricsItem
@ -12,10 +12,7 @@
<div class="analysis min-w-[410px]">
<ExecuteAnalysis :detail="detail" />
</div>
</div>
<div class="analysis-wrapper">
<div class="analysis min-w-[330px]">
<div v-if="functionalCaseTotal" class="analysis min-w-[330px]">
<div class="block-title">{{ t('report.detail.useCaseAnalysis') }}</div>
<div class="flex">
<div class="w-[70%]">
@ -45,8 +42,7 @@
</div>
</div>
</div>
<!-- TODO 接口用例&场景用例待联调 -->
<div class="analysis min-w-[330px]">
<div v-if="apiCaseTotal" class="analysis min-w-[330px]">
<div class="block-title">{{ t('report.detail.apiUseCaseAnalysis') }}</div>
<div class="flex">
<div class="w-[70%]">
@ -76,7 +72,7 @@
</div>
</div>
</div>
<div class="analysis min-w-[330px]">
<div v-if="scenarioCaseTotal" class="analysis min-w-[330px]">
<div class="block-title">{{ t('report.detail.scenarioUseCaseAnalysis') }}</div>
<div class="flex">
<div class="w-[70%]">
@ -107,6 +103,7 @@
</div>
</div>
</div>
<Summary
v-model:richText="richText"
:share-id="shareId"
@ -297,7 +294,7 @@
icon: 'threshold',
},
{
name: t('report.detail.reportPassRate'),
name: t('report.passRate'),
value: detail.value.passRate,
unit: '%',
icon: 'passRate',
@ -330,6 +327,42 @@
const apiScenarioDetail = getSummaryDetail(detail.value.apiScenarioCount || defaultCount);
return apiScenarioDetail.successRate;
});
const functionalCaseTotal = computed(() => getSummaryDetail(detail.value.functionalCount).caseTotal);
const apiCaseTotal = computed(() => getSummaryDetail(detail.value.apiCaseCount).caseTotal);
const scenarioCaseTotal = computed(() => getSummaryDetail(detail.value.apiScenarioCount).caseTotal);
const getFunctionalTab = computed(() => {
return functionalCaseTotal.value
? [
{
value: 'featureCase',
label: t('report.detail.featureCaseDetails'),
},
]
: [];
});
const getApiTab = computed(() => {
return apiCaseTotal.value
? [
{
value: 'apiCase',
label: t('report.detail.apiCaseDetails'),
},
]
: [];
});
const getScenarioTab = computed(() => {
return scenarioCaseTotal.value
? [
{
value: 'scenarioCase',
label: t('report.detail.scenarioCaseDetails'),
},
]
: [];
});
const activeTab = ref('bug');
const contentTabList = ref([
@ -337,20 +370,22 @@
value: 'bug',
label: t('report.detail.bugDetails'),
},
{
value: 'featureCase',
label: t('report.detail.featureCaseDetails'),
},
{
value: 'apiCase',
label: t('report.detail.apiCaseDetails'),
},
{
value: 'scenarioCase',
label: t('report.detail.scenarioCaseDetails'),
},
...getFunctionalTab.value,
...getApiTab.value,
...getScenarioTab.value,
]);
const cardCount = computed(() => {
const totalList = [functionalCaseTotal.value, apiCaseTotal.value, scenarioCaseTotal.value];
let count = 2;
totalList.forEach((item: number) => {
if (item > 0) {
count++;
}
});
return count;
});
watchEffect(() => {
if (props.detailInfo) {
detail.value = cloneDeep(props.detailInfo);
@ -383,12 +418,12 @@
@apply mb-4 font-medium;
}
.analysis-wrapper {
@apply mb-4 flex flex-wrap items-center gap-4;
@apply mb-4 grid items-center gap-4;
.analysis {
padding: 24px;
height: 250px;
box-shadow: 0 0 10px rgba(120 56 135/ 5%);
@apply flex-1 rounded-xl bg-white;
@apply rounded-xl bg-white;
.charts {
top: 36%;
right: 0;
@ -398,5 +433,23 @@
margin: auto;
}
}
&[data-cards='2'],
&[data-cards='4'] {
grid-template-columns: repeat(2, 1fr);
}
&[data-cards='3'] {
grid-template-columns: repeat(3, 1fr);
}
// 523
&[data-cards='5'] {
grid-template-columns: repeat(6, 1fr);
& > .analysis:nth-child(1),
& > .analysis:nth-child(2) {
grid-column: span 3;
}
& > .analysis:nth-child(n + 3) {
grid-column: span 2;
}
}
}
</style>

View File

@ -94,11 +94,11 @@
const allSuccessRate = `${Number.isNaN(allSuccessCount) ? 0 : allSuccessCount.toFixed(2)}%`;
// TODO
if (props.isPlanGroup) {
return `<p style=""><span color="" fontsize=""> <strong>${props.detail.name}</strong>包含 ${props.detail.planCount}个子计划。
return `<p style=""><span color="" fontsize=""> <strong>${props.detail.testPlanName}</strong>包含 ${props.detail.planCount}个子计划。
其中 ${props.detail.passCountOfPlan} 个子计划通过 ${props.detail.failCountOfPlan} 个子计划不通过</span></p>`;
}
//
return `<p style=""><span color="" fontsize=""> <strong>${props.detail.name}</strong> 包含功能测试、接口用例、场景用例, 共 ${allCaseTotal}条用例,已执行 ${allHasExecutedCase} 条,通过用例 ${allSuccessCase} 条,通过率为 ${allSuccessRate},达到/未达到通过阈值(通过阈值为${props.detail.passThreshold}%<strong>${props.detail.name}</strong> 计划满足/不满足发布要求。<br>
return `<p style=""><span color="" fontsize=""> <strong>${props.detail.testPlanName}</strong> 包含功能测试、接口用例、场景用例, 共 ${allCaseTotal}条用例,已执行 ${allHasExecutedCase} 条,通过用例 ${allSuccessCase} 条,通过率为 ${allSuccessRate},达到/未达到通过阈值(通过阈值为${props.detail.passThreshold}%<strong>${props.detail.testPlanName}</strong> 计划满足/不满足发布要求。<br>
1本次测试包含${functionalCaseDetail.caseTotal}条功能测试用例执行了${functionalCaseDetail.hasExecutedCase}未执行${functionalCaseDetail.pending}执行率为${functionalCaseDetail.apiExecutedRate}通过用例${functionalCaseDetail.success}通过率为${functionalCaseDetail.successRate}共发现缺陷${props.detail.functionalBugCount}<br>
2本次测试包含${apiCaseDetail.caseTotal}条接口测试用例执行了${apiCaseDetail.hasExecutedCase}未执行${apiCaseDetail.pending}执行率为${apiCaseDetail.apiExecutedRate}通过用例${apiCaseDetail.success}通过率为${apiCaseDetail.successRate}共发现缺陷 ${props.detail.apiBugCount} <br>
3本次测试包含${apiScenarioDetail.caseTotal}条场景测试用例执行了${apiScenarioDetail.hasExecutedCase}未执行${apiScenarioDetail.pending}执行率为${apiScenarioDetail.apiExecutedRate}%通过用例${apiScenarioDetail.success}通过率为${apiScenarioDetail.successRate}共发现缺陷${props.detail.scenarioBugCount}</span></p>

View File

@ -29,7 +29,6 @@ export default {
'report.detail.featureCaseDetails': 'Feature case details',
'report.detail.executionAnalysis': 'Execution Analysis',
'report.detail.threshold': 'Pass threshold',
'report.detail.reportPassRate': 'The report pass',
'report.detail.performCompletion': 'Perform completion',
'report.detail.totalDefects': 'Total defects',
'report.detail.useCaseAnalysis': 'Function of use case analysis',

View File

@ -29,7 +29,6 @@ export default {
'report.detail.featureCaseDetails': '功能用例明细',
'report.detail.executionAnalysis': '执行分析',
'report.detail.threshold': '通过阈值',
'report.detail.reportPassRate': '报告通过率',
'report.detail.performCompletion': '执行完成率',
'report.detail.totalDefects': '缺陷总数',
'report.detail.useCaseAnalysis': '功能用例分析',

View File

@ -165,6 +165,7 @@
},
...form.value,
type: props.showType,
editColumn: 'TAGS',
};
await batchEditTestPlan(params);
Message.success(t('caseManagement.featureCase.editSuccess'));

View File

@ -452,7 +452,6 @@
const tableStore = useTableStore();
const appStore = useAppStore();
const router = useRouter();
const route = useRoute();
const { t } = useI18n();
const { openModal } = useModal();
@ -888,10 +887,6 @@
selectAll: !!batchParams.value?.selectAll,
selectIds: batchParams.value.selectedIds || [],
keyword: keyword.value,
condition: {
filter: filterParams,
keyword: keyword.value,
},
filter: filterParams,
combine: {
...batchParams.value.condition,