feat(接口测试): 报告样式调整&导入同步 mock

This commit is contained in:
baiqi 2024-09-11 18:20:43 +08:00 committed by Craftsman
parent 3c5d0c3d51
commit 0e882485b7
9 changed files with 176 additions and 146 deletions

View File

@ -229,6 +229,7 @@ export interface ImportApiDefinitionRequest {
coverModule: boolean; // 是否覆盖子目录 coverModule: boolean; // 是否覆盖子目录
coverData: boolean; // 是否覆盖数据 coverData: boolean; // 是否覆盖数据
syncCase: boolean; // 是否同步导入用例 syncCase: boolean; // 是否同步导入用例
syncMock: boolean; // 是否同步导入mock
protocol: string; protocol: string;
authSwitch?: boolean; authSwitch?: boolean;
authUsername?: string; authUsername?: string;

View File

@ -100,6 +100,13 @@
<a-switch v-model:model-value="importForm.syncCase" size="small" /> <a-switch v-model:model-value="importForm.syncCase" size="small" />
{{ t('apiTestManagement.syncImportCase') }} {{ t('apiTestManagement.syncImportCase') }}
</div> </div>
<div
v-if="importForm.platform === RequestImportFormat.MeterSphere"
class="mb-[16px] flex items-center gap-[4px]"
>
<a-switch v-model:model-value="importForm.syncMock" size="small" />
{{ t('apiTestManagement.syncImportMock') }}
</div>
<a-form-item <a-form-item
v-if="importForm.platform === RequestImportFormat.SWAGGER" v-if="importForm.platform === RequestImportFormat.SWAGGER"
:label="t('apiTestManagement.importMethod')" :label="t('apiTestManagement.importMethod')"
@ -395,6 +402,7 @@
moduleId: '', moduleId: '',
coverData: false, coverData: false,
syncCase: true, syncCase: true,
syncMock: true,
coverModule: false, coverModule: false,
swaggerUrl: '', swaggerUrl: '',
authSwitch: false, authSwitch: false,
@ -480,6 +488,7 @@
coverModule: importForm.value.coverModule, coverModule: importForm.value.coverModule,
coverData: importForm.value.coverData, coverData: importForm.value.coverData,
syncCase: importForm.value.syncCase, syncCase: importForm.value.syncCase,
syncMock: importForm.value.syncMock,
protocol: importForm.value.protocol, protocol: importForm.value.protocol,
moduleId: importForm.value.moduleId, moduleId: importForm.value.moduleId,
authSwitch: importForm.value.authSwitch, authSwitch: importForm.value.authSwitch,
@ -496,6 +505,7 @@
coverModule: importForm.value.coverModule, coverModule: importForm.value.coverModule,
coverData: importForm.value.coverData, coverData: importForm.value.coverData,
syncCase: importForm.value.syncCase, syncCase: importForm.value.syncCase,
syncMock: importForm.value.syncMock,
protocol: importForm.value.protocol, protocol: importForm.value.protocol,
moduleId: importForm.value.moduleId, moduleId: importForm.value.moduleId,
swaggerUrl: importForm.value.swaggerUrl, swaggerUrl: importForm.value.swaggerUrl,
@ -528,6 +538,7 @@
coverModule: importForm.value.coverModule, coverModule: importForm.value.coverModule,
coverData: importForm.value.coverData, coverData: importForm.value.coverData,
syncCase: importForm.value.syncCase, syncCase: importForm.value.syncCase,
syncMock: importForm.value.syncMock,
protocol: importForm.value.protocol, protocol: importForm.value.protocol,
moduleId: importForm.value.moduleId, moduleId: importForm.value.moduleId,
swaggerUrl: importForm.value.swaggerUrl, swaggerUrl: importForm.value.swaggerUrl,

View File

@ -84,6 +84,7 @@ export default {
'apiTestManagement.exportCase': 'Synchronous export use case', 'apiTestManagement.exportCase': 'Synchronous export use case',
'apiTestManagement.exportMock': 'Export Mock Synchronously', 'apiTestManagement.exportMock': 'Export Mock Synchronously',
'apiTestManagement.syncImportCase': 'Sync Import API Cases', 'apiTestManagement.syncImportCase': 'Sync Import API Cases',
'apiTestManagement.syncImportMock': 'Sync Import API Mock',
'apiTestManagement.syncUpdateDirectory': 'Sync Update API Directory', 'apiTestManagement.syncUpdateDirectory': 'Sync Update API Directory',
'apiTestManagement.importSwaggerFileTip1': 'Supports Swagger 3.0 version JSON files,', 'apiTestManagement.importSwaggerFileTip1': 'Supports Swagger 3.0 version JSON files,',
'apiTestManagement.importSwaggerFileTip2': '2.0 files can be converted to 3.0 on the official website', 'apiTestManagement.importSwaggerFileTip2': '2.0 files can be converted to 3.0 on the official website',

View File

@ -80,6 +80,7 @@ export default {
'apiTestManagement.exportCase': '同步导出用例', 'apiTestManagement.exportCase': '同步导出用例',
'apiTestManagement.exportMock': '同步导出 Mock', 'apiTestManagement.exportMock': '同步导出 Mock',
'apiTestManagement.syncImportCase': '同步导入接口用例', 'apiTestManagement.syncImportCase': '同步导入接口用例',
'apiTestManagement.syncImportMock': '同步导入 Mock',
'apiTestManagement.syncUpdateDirectory': '同步更新接口所在目录', 'apiTestManagement.syncUpdateDirectory': '同步更新接口所在目录',
'apiTestManagement.importSwaggerFileTip1': '支持 Swagger 3.0 版本的 json 文件,', 'apiTestManagement.importSwaggerFileTip1': '支持 Swagger 3.0 版本的 json 文件,',
'apiTestManagement.importSwaggerFileTip2': '2.0 文件可以在官网一键转换 3.0', 'apiTestManagement.importSwaggerFileTip2': '2.0 文件可以在官网一键转换 3.0',

View File

@ -34,7 +34,7 @@
<!-- </a-popover> --> <!-- </a-popover> -->
</div> </div>
<div class="chart-legend grid flex-1 gap-y-3"> <div class="chart-legend grid flex-1 gap-y-[12px]">
<!-- 图例开始 --> <!-- 图例开始 -->
<div v-for="item of legendData" :key="item.value" class="grid grid-cols-3"> <div v-for="item of legendData" :key="item.value" class="grid grid-cols-3">
<div class="flex flex-nowrap items-center"> <div class="flex flex-nowrap items-center">

View File

@ -4,151 +4,155 @@
<ReportDetailHeader :detail="detail" show-type="CASE" /> <ReportDetailHeader :detail="detail" show-type="CASE" />
<!-- 报告参数结束 --> <!-- 报告参数结束 -->
<!-- 报告分析开始 --> <!-- 报告分析开始 -->
<div class="analyze mb-1"> <div class="analyze">
<!-- 请求分析 --> <!-- 请求分析 -->
<div class="request-analyze min-h-[110px]"> <div class="block-title">{{ t('report.detail.api.requestAnalysis') }}</div>
<div class="block-title mb-4">{{ t('report.detail.api.requestAnalysis') }}</div> <div class="flex justify-between">
<SetReportChart <div class="request-analyze">
:legend-data="legendData" <SetReportChart
:options="charOptions" :legend-data="legendData"
:request-total="getIndicators(detail.total) || 0" :options="charOptions"
/> :request-total="getIndicators(detail.total) || 0"
</div> />
<!-- 耗时分析 --> </div>
<div class="time-analyze"> <!-- 耗时分析 -->
<div class="time-card mb-2 mt-[16px] h-[40px] flex-1 gap-4"> <div class="time-analyze gap-[12px]">
<div class="time-card-item flex h-full"> <div class="time-card flex-1 gap-[12px]">
<MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" /> <div class="time-card-item">
<span class="time-card-item-title">{{ t('report.detail.api.totalTime') }}</span> <MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
<a-popover position="bottom" content-class="response-popover-content"> <span class="time-card-item-title">{{ t('report.detail.api.totalTime') }}</span>
<span class="count">{{ getTotalTime.split('-')[0] }}</span <a-popover position="bottom" content-class="response-popover-content">
><span class="time-card-item-title">{{ getTotalTime.split('-')[1] || 'ms' }}</span> <span class="count">{{ getTotalTime.split('-')[0] }}</span
<template #content> ><span class="time-card-item-title">{{ getTotalTime.split('-')[1] || 'ms' }}</span>
<div class="min-w-[140px] max-w-[400px] p-4 text-[14px]"> <template #content>
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.totalTime') }}</div> <div class="min-w-[140px] max-w-[400px] p-4 text-[14px]">
<div class="mt-2 text-[var(--color-text-1)]"> <div class="text-[var(--color-text-4)]">{{ t('report.detail.api.totalTime') }}</div>
<span class="text-[18px] font-medium">{{ getTotalTime.split('-')[0] }}</span <div class="mt-2 text-[var(--color-text-1)]">
>{{ getTotalTime.split('-')[1] || 'ms' }}</div <span class="text-[18px] font-medium">{{ getTotalTime.split('-')[0] }}</span
> >{{ getTotalTime.split('-')[1] || 'ms' }}</div
>
</div>
</template>
</a-popover>
</div>
<div class="time-card-item h-full">
<MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
<span class="time-card-item-title"> {{ t('report.detail.api.requestTotalTime') }}</span>
<a-popover position="bottom" content-class="response-popover-content">
<div class="flex items-end">
<div class="count">
{{ detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '0' }}
</div>
<div class="time-card-item-title">
{{ detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[1] : 'ms' }}
</div>
</div> </div>
</template>
</a-popover> <template #content>
<div class="min-w-[140px] max-w-[400px] p-4 text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.requestTotalTime') }}</div>
<div class="mt-2 text-[var(--color-text-1)]">
<span class="text-[18px] font-medium">{{
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '0'
}}</span
>{{
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[1] : 'ms'
}}</div
>
</div>
</template>
</a-popover>
</div>
</div> </div>
<div class="time-card-item h-full">
<MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" /> <div class="time-card flex-1 gap-4">
<span class="time-card-item-title"> {{ t('report.detail.api.requestTotalTime') }}</span> <!-- 执行率 -->
<a-popover position="bottom" content-class="response-popover-content"> <div v-if="detail.integrated" class="time-card-item-rote">
<div class="time-card-item-rote-title">
<MsIcon type="icon-icon_yes_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
{{ t('report.detail.api.executionRate') }}
</div>
<div class="flex items-center"> <div class="flex items-center">
<div class="count">{{ <a-popover position="bottom" content-class="response-popover-content">
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '0' <div class="count one-line-text max-w-[80px]"> {{ getExcuteRate() }} </div
}}</div ><span v-show="getExcuteRate() !== 'Calculating'">%</span>
><div class="time-card-item-title">{{ <a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[1] : 'ms' <span>{{ getIndicators(getRequestEacuteCount) }}</span>
}}</div> <span class="mx-1 text-[var(--color-text-4)]">/ {{ getIndicators(getRequestTotalCount) }}</span>
<template #content>
<div class="min-w-[190px] max-w-[400px] p-4 text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.executionRate') }}</div>
<div class="mt-2 flex items-center justify-between">
<div class="count text-[18px] font-medium">
{{ getExcuteRate() }} <span v-show="getExcuteRate() !== 'Calculating'">%</span>
</div>
<div>
<span>{{ getIndicators(getRequestEacuteCount) }}</span>
<span class="mx-1 text-[var(--color-text-4)]"
>/ {{ getIndicators(getRequestTotalCount) }}</span
>
</div>
</div>
</div>
</template>
</a-popover>
</div>
</div>
<div class="time-card-item-rote">
<div class="time-card-item-rote-title">
<MsIcon type="icon-icon_yes_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
{{ t('report.detail.api.assertPass') }}
</div> </div>
<template #content> <div class="flex items-center">
<div class="min-w-[140px] max-w-[400px] p-4 text-[14px]"> <a-popover position="bottom" content-class="response-popover-content">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.requestTotalTime') }}</div> <div class="flex items-center">
<div class="mt-2 text-[var(--color-text-1)]"> <div class="count one-line-text max-w-[80px]">{{ detail.assertionPassRate || '0.00' }}</div
<span class="text-[18px] font-medium">{{ ><span v-show="detail.assertionPassRate !== 'Calculating'" class="ml-1">%</span>
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '0' <a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
}}</span <div class="one-line-text max-w-[80px]">{{
>{{ getIndicators(detail.assertionSuccessCount) !== 'Calculating'
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[1] : 'ms' ? addCommasToNumber(detail.assertionSuccessCount || 0)
}}</div : getIndicators(detail.assertionSuccessCount)
> }}</div>
</div> <span class="mx-1 text-[var(--color-text-4)]">/</span>
</template> <div class="one-line-text max-w-[80px]">
</a-popover> {{
</div> getIndicators(detail.assertionCount) !== 'Calculating'
</div> ? addCommasToNumber(detail.assertionCount)
: getIndicators(detail.assertionCount)
<div class="time-card flex-1 gap-4"> }}</div
<!-- 执行率 --> >
<div v-if="detail.integrated" class="time-card-item-rote">
<div class="time-card-item-rote-title">
<MsIcon type="icon-icon_yes_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
{{ t('report.detail.api.executionRate') }}
</div>
<div class="flex items-center">
<a-popover position="bottom" content-class="response-popover-content">
<div class="count one-line-text max-w-[80px]"> {{ getExcuteRate() }} </div
><span v-show="getExcuteRate() !== 'Calculating'">%</span>
<a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
<span>{{ getIndicators(getRequestEacuteCount) }}</span>
<span class="mx-1 text-[var(--color-text-4)]">/ {{ getIndicators(getRequestTotalCount) }}</span>
<template #content>
<div class="min-w-[190px] max-w-[400px] p-4 text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.executionRate') }}</div>
<div class="mt-2 flex items-center justify-between">
<div class="count text-[18px] font-medium">
{{ getExcuteRate() }} <span v-show="getExcuteRate() !== 'Calculating'">%</span>
</div>
<div>
<span>{{ getIndicators(getRequestEacuteCount) }}</span>
<span class="mx-1 text-[var(--color-text-4)]">/ {{ getIndicators(getRequestTotalCount) }}</span>
</div>
</div>
</div> </div>
</template>
</a-popover>
</div>
</div>
<div class="time-card-item-rote">
<div class="time-card-item-rote-title">
<MsIcon type="icon-icon_yes_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
{{ t('report.detail.api.assertPass') }}
</div>
<div class="flex items-center"> <template #content>
<a-popover position="bottom" content-class="response-popover-content"> <div class="min-w-[190px] max-w-[400px] p-4 text-[14px]">
<div class="flex items-center"> <div class="text-[var(--color-text-4)]">{{ t('report.detail.api.assertPass') }}</div>
<div class="count one-line-text max-w-[80px]">{{ detail.assertionPassRate || '0.00' }}</div <div class="mt-2 flex items-center justify-between">
><span v-show="detail.assertionPassRate !== 'Calculating'" class="ml-1">%</span> <div class="text-[18px] font-medium text-[var(--color-text-1)]"
<a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider> >{{ getIndicators(detail.assertionPassRate) }}
<div class="one-line-text max-w-[80px]">{{ <span v-show="detail.assertionPassRate !== 'Calculating'">%</span></div
getIndicators(detail.assertionSuccessCount) !== 'Calculating'
? addCommasToNumber(detail.assertionSuccessCount || 0)
: getIndicators(detail.assertionSuccessCount)
}}</div>
<span class="mx-1 text-[var(--color-text-4)]">/</span>
<div class="one-line-text max-w-[80px]">
{{
getIndicators(detail.assertionCount) !== 'Calculating'
? addCommasToNumber(detail.assertionCount)
: getIndicators(detail.assertionCount)
}}</div
>
</div>
<template #content>
<div class="min-w-[190px] max-w-[400px] p-4 text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.assertPass') }}</div>
<div class="mt-2 flex items-center justify-between">
<div class="text-[18px] font-medium text-[var(--color-text-1)]"
>{{ getIndicators(detail.assertionPassRate) }}
<span v-show="detail.assertionPassRate !== 'Calculating'">%</span></div
>
<div>
<span class="text-[var(--color-text-1)]">{{
getIndicators(detail.assertionSuccessCount) !== 'Calculating'
? addCommasToNumber(detail.assertionSuccessCount || 0)
: getIndicators(detail.assertionSuccessCount)
}}</span>
<span class="text-[var(--color-text-4)]"
><span class="mx-1">/</span>
{{
getIndicators(detail.assertionCount) !== 'Calculating'
? addCommasToNumber(detail.assertionCount)
: getIndicators(detail.assertionCount)
}}</span
> >
<div>
<span class="text-[var(--color-text-1)]">{{
getIndicators(detail.assertionSuccessCount) !== 'Calculating'
? addCommasToNumber(detail.assertionSuccessCount || 0)
: getIndicators(detail.assertionSuccessCount)
}}</span>
<span class="text-[var(--color-text-4)]"
><span class="mx-1">/</span>
{{
getIndicators(detail.assertionCount) !== 'Calculating'
? addCommasToNumber(detail.assertionCount)
: getIndicators(detail.assertionCount)
}}</span
>
</div>
</div> </div>
</div> </div>
</div> </template>
</template> </a-popover>
</a-popover> </div>
</div> </div>
</div> </div>
</div> </div>
@ -417,11 +421,12 @@
@apply mb-4 bg-white; @apply mb-4 bg-white;
} }
.analyze { .analyze {
height: 196px; @apply mb-4 bg-white;
padding: 16px;
border-radius: 4px; border-radius: 4px;
@apply mb-4 flex justify-between bg-white;
.request-analyze { .request-analyze {
@apply flex h-full flex-1 flex-col p-4; @apply flex h-full flex-1 flex-col;
.chart-legend { .chart-legend {
.chart-legend-item { .chart-legend-item {
@apply grid grid-cols-3 gap-2; @apply grid grid-cols-3 gap-2;
@ -435,28 +440,36 @@
} }
} }
.time-analyze { .time-analyze {
@apply flex h-full flex-1 flex-col p-4; @apply flex h-full flex-1 flex-col px-4;
.time-card { .time-card {
@apply flex items-center justify-between; @apply flex items-center justify-between;
.time-card-item { .time-card-item {
@apply flex flex-1 flex-grow items-end;
padding: 9px 12px;
border-radius: 6px; border-radius: 6px;
background: var(--color-text-n9); background: var(--color-text-n9);
@apply mt-4 flex flex-1 flex-grow items-center px-4;
.time-card-item-title { .time-card-item-title {
color: var(--color-text-4); color: var(--color-text-4);
line-height: 16px;
} }
.count { .count {
font-size: 18px;
@apply mx-2 font-medium; @apply mx-2 font-medium;
line-height: 22px;
font-size: 18px;
} }
} }
.time-card-item-rote { .time-card-item-rote {
@apply flex flex-1 flex-grow flex-col;
padding: 9px 12px;
border-radius: 6px; border-radius: 6px;
background: var(--color-text-n9); background: var(--color-text-n9);
@apply mt-4 flex flex-1 flex-grow flex-col p-4;
.time-card-item-rote-title { .time-card-item-rote-title {
@apply mb-2 flex items-center;
color: var(--color-text-4); color: var(--color-text-4);
@apply mb-2;
} }
.count { .count {
font-size: 18px; font-size: 18px;
@ -473,7 +486,9 @@
} }
} }
.block-title { .block-title {
font-size: 14px;
@apply font-medium; @apply font-medium;
margin-bottom: 16px;
font-size: 14px;
} }
</style> </style>

View File

@ -48,6 +48,7 @@
:active-type="activeTab" :active-type="activeTab"
:report-detail="detail || []" :report-detail="detail || []"
:is-export="props.isExport" :is-export="props.isExport"
class="p-[16px]"
/> />
</div> </div>
<!-- 报告明细结束 --> <!-- 报告明细结束 -->

View File

@ -382,7 +382,7 @@
.arco-tree-node-title { .arco-tree-node-title {
@apply !cursor-pointer bg-white; @apply !cursor-pointer bg-white;
padding: 12px 4px; padding: 8px 4px;
&:hover { &:hover {
background-color: white !important; background-color: white !important;
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div <div
class="tiled-wrap p-4" class="tiled-wrap"
:class="{ :class="{
'border border-solid border-[var(--color-text-n8)]': props.showType === 'API', 'border border-solid border-[var(--color-text-n8)]': props.showType === 'API',
'!max-h-max': props.isExport, '!max-h-max': props.isExport,