feat(测试计划): 脑图失败重试&报告展示重试结果
This commit is contained in:
parent
420a826697
commit
fe28bcc27d
|
@ -183,7 +183,11 @@
|
|||
</a-form-item>
|
||||
<a-form-item class="hidden-item">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-switch v-model:model-value="configForm.retryOnFail" size="small"></a-switch>
|
||||
<a-switch
|
||||
v-model:model-value="configForm.retryOnFail"
|
||||
:disabled="configForm.level === 2 && configForm.extended"
|
||||
size="small"
|
||||
></a-switch>
|
||||
<div>{{ t('ms.minders.failRetry') }}</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
@ -201,6 +205,7 @@
|
|||
:step="1"
|
||||
:min="1"
|
||||
:precision="0"
|
||||
:disabled="configForm.level === 2 && configForm.extended"
|
||||
size="small"
|
||||
class="w-[120px]"
|
||||
></a-input-number>
|
||||
|
@ -218,6 +223,7 @@
|
|||
:step="100"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
:disabled="configForm.level === 2 && configForm.extended"
|
||||
size="small"
|
||||
class="w-[120px]"
|
||||
></a-input-number>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
:class="[props.class, props.noContent ? 'no-content' : '']"
|
||||
@change="(val) => handleTabClick(val as string)"
|
||||
>
|
||||
<a-tab-pane v-for="item of props.contentTabList" :key="item.value" :title="item.label">
|
||||
<a-tab-pane v-for="item of props.contentTabList" :key="item.value" :title="`${item.label}`">
|
||||
<template v-if="props.showBadge" #title>
|
||||
<a-badge
|
||||
v-if="props.getTextFunc(item.value) !== ''"
|
||||
|
@ -27,8 +27,11 @@
|
|||
<div
|
||||
v-for="item of props.contentTabList"
|
||||
:key="item.value"
|
||||
class="ms-tab--button-item"
|
||||
:class="item.value === tempActiveKey ? 'ms-tab--button-item--active' : ''"
|
||||
class="ms-tab-button-item"
|
||||
:class="[
|
||||
item.value === tempActiveKey ? 'ms-tab-button-item--active' : '',
|
||||
props.buttonSize === 'small' ? 'ms-tab--button-item--small' : '',
|
||||
]"
|
||||
@click="handleTabClick(item.value)"
|
||||
>
|
||||
{{ item.label }}
|
||||
|
@ -40,23 +43,28 @@
|
|||
const props = withDefaults(
|
||||
defineProps<{
|
||||
mode?: 'origin' | 'button';
|
||||
contentTabList: { label: string; value: string }[];
|
||||
contentTabList: { label: string | number; value: string | number }[];
|
||||
class?: string;
|
||||
getTextFunc?: (value: any) => string;
|
||||
noContent?: boolean;
|
||||
showBadge?: boolean;
|
||||
changeInterceptor?: (newVal: string, oldVal: string, done: () => void) => void;
|
||||
changeInterceptor?: (newVal: string | number, oldVal: string | number, done: () => void) => void;
|
||||
buttonSize?: 'small' | 'default';
|
||||
}>(),
|
||||
{
|
||||
mode: 'origin',
|
||||
showBadge: true,
|
||||
getTextFunc: (value: any) => value,
|
||||
class: '',
|
||||
buttonSize: 'default',
|
||||
}
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'change', value: string | number): void;
|
||||
}>();
|
||||
|
||||
// 实际值,用于最终确认修改的 tab 值
|
||||
const activeKey = defineModel<string>('activeKey', {
|
||||
const activeKey = defineModel<string | number>('activeKey', {
|
||||
default: '',
|
||||
});
|
||||
// 临时值,用于组件内部变更,但未影响到实际值
|
||||
|
@ -69,7 +77,7 @@
|
|||
}
|
||||
);
|
||||
|
||||
function handleTabClick(value: string) {
|
||||
function handleTabClick(value: string | number) {
|
||||
if (value === activeKey.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -85,6 +93,7 @@
|
|||
// 不存在拦截器,直接修改实际值
|
||||
activeKey.value = value;
|
||||
}
|
||||
emit('change', activeKey.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -107,7 +116,7 @@
|
|||
@apply flex;
|
||||
|
||||
border-radius: var(--border-radius-small);
|
||||
.ms-tab--button-item {
|
||||
.ms-tab-button-item {
|
||||
@apply cursor-pointer;
|
||||
|
||||
padding: 4px 12px;
|
||||
|
@ -128,7 +137,12 @@
|
|||
color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
.ms-tab--button-item--active {
|
||||
.ms-tab--button-item--small {
|
||||
padding: 1px 12px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.ms-tab-button-item--active {
|
||||
z-index: 2;
|
||||
border: 1px solid rgb(var(--primary-5)) !important;
|
||||
color: rgb(var(--primary-5));
|
||||
|
|
|
@ -1,33 +1,43 @@
|
|||
<template>
|
||||
<div class="flex flex-col" @click.stop="() => {}">
|
||||
<div class="response-header">
|
||||
<div v-if="isShowLoopControl" class="flex w-full items-center justify-start bg-white p-4" @click.stop="() => {}">
|
||||
<a-pagination
|
||||
v-model:page-size="controlPageSize"
|
||||
v-model:current="controlCurrent"
|
||||
:total="controlTotal"
|
||||
size="mini"
|
||||
show-total
|
||||
:show-jumper="controlTotal > 5"
|
||||
<div
|
||||
v-if="isShowLoopControl"
|
||||
class="flex w-full items-center justify-start bg-white pb-[8px] pt-[16px]"
|
||||
@click.stop="() => {}"
|
||||
>
|
||||
<MsTab
|
||||
v-if="isFailedRetry"
|
||||
v-model:activeKey="controlCurrent"
|
||||
:content-tab-list="controlTotalList"
|
||||
mode="button"
|
||||
button-size="small"
|
||||
@change="loadControlLoop"
|
||||
/>
|
||||
<loopPagination
|
||||
v-else
|
||||
v-model:current-loop="controlCurrent"
|
||||
:loop-total="controlTotal"
|
||||
class="!mb-0"
|
||||
@change="loadControlLoop"
|
||||
/>
|
||||
<!-- <loopPagination v-model:current-loop="controlCurrent" :loop-total="controlTotal" /> -->
|
||||
</div>
|
||||
<div class="flex w-full items-center justify-between rounded bg-[var(--color-text-n9)] p-4">
|
||||
<div class="font-medium">
|
||||
<span
|
||||
:class="{ 'text-[rgb(var(--primary-5))]': activeType === 'ResContent' }"
|
||||
@click.stop="setActiveType('ResContent')"
|
||||
>{{ t('report.detail.api.resContent') }}</span
|
||||
>
|
||||
{{ t('report.detail.api.resContent') }}
|
||||
</span>
|
||||
<span
|
||||
v-if="total > 0"
|
||||
:class="{ 'text-[rgb(var(--primary-5))]': activeType === 'SubRequest' }"
|
||||
@click.stop="setActiveType('SubRequest')"
|
||||
>
|
||||
<a-divider direction="vertical" :margin="8"></a-divider>
|
||||
{{ t('report.detail.api.subRequest') }}</span
|
||||
>
|
||||
{{ t('report.detail.api.subRequest') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-row gap-6 text-center">
|
||||
<a-popover position="left" content-class="response-popover-content">
|
||||
|
@ -121,7 +131,9 @@
|
|||
import { useRoute } from 'vue-router';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||
import result from '@/views/api-test/components/requestComposition/response/result.vue';
|
||||
import loopPagination from '@/views/api-test/scenario/components/common/loopPagination.vue';
|
||||
|
||||
import { reportCaseStepDetail, reportStepDetail } from '@/api/modules/api-test/report';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -337,7 +349,24 @@
|
|||
}
|
||||
return 0;
|
||||
});
|
||||
const controlPageSize = ref(1);
|
||||
const controlTotalList = computed(() => {
|
||||
return Array.from({ length: controlTotal.value }, (v, k) => {
|
||||
if (k === 0) {
|
||||
return {
|
||||
value: k + 1,
|
||||
label: t('apiTestDebug.first'),
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: k + 1,
|
||||
label: `${t('apiTestDebug.retry')} ${k}`,
|
||||
};
|
||||
});
|
||||
});
|
||||
const isFailedRetry = computed(() => {
|
||||
return !!props.stepItem?.stepChildren?.some((item) => item.requestName.includes('MsRetry_')); // 是否包含重试前缀
|
||||
});
|
||||
|
||||
/**
|
||||
* 循环次数控制器
|
||||
*/
|
||||
|
|
|
@ -213,4 +213,6 @@ export default {
|
|||
'apiTestDebug.standardAdditionsTip':
|
||||
'Writing format: parameter name, type, required, parameter value; multiple records separated by newlines',
|
||||
'apiTestDebug.quickAdditions': 'Quick',
|
||||
'apiTestDebug.first': 'First',
|
||||
'apiTestDebug.retry': 'Retry',
|
||||
};
|
||||
|
|
|
@ -199,4 +199,6 @@ export default {
|
|||
'apiTestDebug.standardAdditions': '标准添加',
|
||||
'apiTestDebug.standardAdditionsTip': '书写格式:参数名,类型,必填,参数值;多条记录换行分隔',
|
||||
'apiTestDebug.quickAdditions': '快捷添加',
|
||||
'apiTestDebug.first': '首次',
|
||||
'apiTestDebug.retry': '重试',
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="mb-4 flex h-[36px] items-center justify-between">
|
||||
<div class="flex h-[36px] items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-2 font-medium leading-[36px]">{{ t('report.detail.api.reportDetail') }}</div>
|
||||
<a-radio-group v-model:model-value="innerActiveTab" type="button" size="small">
|
||||
|
|
|
@ -128,25 +128,21 @@
|
|||
<span v-show="step.requestTime !== null" class="resTime">
|
||||
{{ t('report.detail.api.responseTime') }}
|
||||
<a-popover position="left" content-class="response-popover-content">
|
||||
<span class="resTimeCount ml-2"
|
||||
>{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-'
|
||||
}}{{
|
||||
step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms'
|
||||
}}</span
|
||||
>
|
||||
<span class="resTimeCount ml-2">
|
||||
{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-' }}
|
||||
{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms' }}
|
||||
</span>
|
||||
<template #content>
|
||||
<span v-show="step.requestTime !== null" class="resTime">
|
||||
{{ t('report.detail.api.responseTime') }}
|
||||
<span class="resTimeCount ml-2"
|
||||
>{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-'
|
||||
}}{{
|
||||
step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms'
|
||||
}}</span
|
||||
></span
|
||||
>
|
||||
<span class="resTimeCount ml-2">
|
||||
{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[0] : '-' }}
|
||||
{{ step.requestTime !== null ? formatDuration(step.requestTime).split('-')[1] : 'ms' }}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</a-popover></span
|
||||
>
|
||||
</a-popover>
|
||||
</span>
|
||||
<span v-show="step.responseSize !== null" class="resSize">
|
||||
{{ t('report.detail.api.responseSize') }}
|
||||
<a-popover position="left" content-class="response-popover-content">
|
||||
|
@ -157,8 +153,8 @@
|
|||
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
|
||||
>
|
||||
</template>
|
||||
</a-popover></span
|
||||
>
|
||||
</a-popover>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,10 +5,18 @@
|
|||
'border border-solid border-[var(--color-text-n8)]': props.showType === 'API',
|
||||
}"
|
||||
>
|
||||
<div v-if="isFailedRetry" class="mb-[8px]">
|
||||
<MsTab
|
||||
v-model:activeKey="controlCurrent"
|
||||
:content-tab-list="controlTotalList"
|
||||
mode="button"
|
||||
button-size="small"
|
||||
/>
|
||||
</div>
|
||||
<!-- 步骤树 -->
|
||||
<StepTree
|
||||
ref="stepTreeRef"
|
||||
v-model:steps="tiledList"
|
||||
v-model:steps="currentTiledList"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
:show-type="props.showType"
|
||||
:active-type="props.activeType"
|
||||
|
@ -36,9 +44,12 @@
|
|||
import { ref } from 'vue';
|
||||
import { cloneDeep, debounce } from 'lodash-es';
|
||||
|
||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||
import StepDrawer from './step/stepDrawer.vue';
|
||||
import StepTree from './step/stepTree.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import type { ReportDetail, ScenarioItemType } from '@/models/apiTest/report';
|
||||
import { ScenarioStepType } from '@/enums/apiEnum';
|
||||
|
||||
|
@ -52,6 +63,8 @@
|
|||
getReportStepDetail?: (...args: any) => Promise<any>; // 获取步骤的详情内容接口
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const tiledList = ref<ScenarioItemType[]>([]);
|
||||
|
||||
const isExpandAll = ref(false); // 是否展开全部
|
||||
|
@ -82,6 +95,37 @@
|
|||
originTreeData.value = cloneDeep(tiledList.value);
|
||||
}
|
||||
|
||||
const controlCurrent = ref(0);
|
||||
const isFailedRetry = computed(() => {
|
||||
// 所有步骤 id 相同且带有重试前缀,说明是单个用例的重试结果
|
||||
return (
|
||||
props.reportDetail.children.every((item) => item.stepId === props.reportDetail.children[0].stepId) &&
|
||||
props.reportDetail.children.some((item) => item.requestName?.includes('MsRetry_'))
|
||||
);
|
||||
});
|
||||
const currentTiledList = computed(() => {
|
||||
if (isFailedRetry.value === false) {
|
||||
// 不是失败重试结果
|
||||
return tiledList.value;
|
||||
}
|
||||
// 失败重试的结果
|
||||
return [tiledList.value[controlCurrent.value]];
|
||||
});
|
||||
const controlTotalList = computed(() => {
|
||||
return Array.from({ length: props.reportDetail.children.length }, (v, k) => {
|
||||
if (k === 0) {
|
||||
return {
|
||||
value: k,
|
||||
label: t('apiTestDebug.first'),
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: k,
|
||||
label: `${t('apiTestDebug.retry')} ${k}`,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.reportDetail,
|
||||
(val) => {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
show-total
|
||||
size="mini"
|
||||
class="loop-pagination"
|
||||
@change="() => emit('change', currentLoop)"
|
||||
>
|
||||
<template #total="{ total }">
|
||||
<div
|
||||
|
@ -31,6 +32,9 @@
|
|||
const props = defineProps<{
|
||||
loopTotal: number;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'change', value: number): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
|
|
@ -90,18 +90,18 @@
|
|||
<template #default="{ detail, loading }">
|
||||
<div ref="wrapperRef" class="bg-white">
|
||||
<div class="header relative h-[48px] border-b pl-2">
|
||||
<div class="max-w-[calc(100%-72px)]"
|
||||
><MsTab
|
||||
<div class="max-w-[calc(100%-72px)]">
|
||||
<MsTab
|
||||
v-model:active-key="activeTab"
|
||||
:content-tab-list="tabSetting"
|
||||
:get-text-func="getTotal"
|
||||
class="no-content relative"
|
||||
@change="clickMenu"
|
||||
/></div>
|
||||
|
||||
<span class="display-setting h-full text-[var(--color-text-2)]" @click="showMenuSetting">{{
|
||||
t('caseManagement.featureCase.detailDisplaySetting')
|
||||
}}</span>
|
||||
/>
|
||||
</div>
|
||||
<span class="display-setting h-full text-[var(--color-text-2)]" @click="showMenuSetting">
|
||||
{{ t('caseManagement.featureCase.detailDisplaySetting') }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
|
@ -247,9 +247,9 @@
|
|||
const commentInputIsActive = computed(() => commentInputRef.value?.isActive);
|
||||
|
||||
const tabSetting = ref<TabItemType[]>([]);
|
||||
const activeTab = ref<string>('detail');
|
||||
const activeTab = ref<string | number>('detail');
|
||||
|
||||
function clickMenu(key: string) {
|
||||
function clickMenu(key: string | number) {
|
||||
activeTab.value = key;
|
||||
featureCaseStore.setActiveTab(key);
|
||||
switch (activeTab.value) {
|
||||
|
|
|
@ -509,7 +509,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
function changeTabInterceptor(newVal: string, oldVal: string, done: () => void) {
|
||||
function changeTabInterceptor(newVal: string | number, oldVal: string | number, done: () => void) {
|
||||
if (oldVal === 'plan' && minderStore.minderUnsaved) {
|
||||
openModal({
|
||||
type: 'warning',
|
||||
|
|
Loading…
Reference in New Issue