feat(测试计划): 脑图失败重试&报告展示重试结果

This commit is contained in:
baiqi 2024-07-10 16:56:17 +08:00 committed by 刘瑞斌
parent 420a826697
commit fe28bcc27d
11 changed files with 149 additions and 52 deletions

View File

@ -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>

View File

@ -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));

View File

@ -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_')); //
});
/**
* 循环次数控制器
*/

View File

@ -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',
};

View File

@ -199,4 +199,6 @@ export default {
'apiTestDebug.standardAdditions': '标准添加',
'apiTestDebug.standardAdditionsTip': '书写格式:参数名,类型,必填,参数值;多条记录换行分隔',
'apiTestDebug.quickAdditions': '快捷添加',
'apiTestDebug.first': '首次',
'apiTestDebug.retry': '重试',
};

View File

@ -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">

View File

@ -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>

View File

@ -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) => {

View File

@ -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();

View File

@ -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) {

View File

@ -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',