feat(接口场景): 场景执行优化&部分问题修复
This commit is contained in:
parent
f195c3595b
commit
0be3be4a28
|
@ -149,7 +149,7 @@
|
||||||
import { ExecuteAssertionConfig } from '@/models/apiTest/common';
|
import { ExecuteAssertionConfig } from '@/models/apiTest/common';
|
||||||
import { ResponseAssertionType, ResponseBodyAssertionType } from '@/enums/apiEnum';
|
import { ResponseAssertionType, ResponseBodyAssertionType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
import { ExecuteAssertion, MsAssertionItem } from './type';
|
import { MsAssertionItem } from './type';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MsAssertion',
|
name: 'MsAssertion',
|
||||||
|
|
|
@ -247,6 +247,7 @@ export enum ScenarioExecuteStatus {
|
||||||
EXECUTING = 'EXECUTING',
|
EXECUTING = 'EXECUTING',
|
||||||
FAILED = 'FAILED',
|
FAILED = 'FAILED',
|
||||||
STOP = 'STOP',
|
STOP = 'STOP',
|
||||||
|
UN_EXECUTE = 'UN_EXECUTE',
|
||||||
}
|
}
|
||||||
// 场景步骤类型
|
// 场景步骤类型
|
||||||
export enum ScenarioStepType {
|
export enum ScenarioStepType {
|
||||||
|
|
|
@ -43,23 +43,6 @@ export interface ApiScenarioGetModuleParams {
|
||||||
refId?: string;
|
refId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 场景修改参数
|
|
||||||
export interface ApiScenarioUpdateDTO {
|
|
||||||
id: string | number;
|
|
||||||
name?: string;
|
|
||||||
priority?: string;
|
|
||||||
status?: ApiScenarioStatus;
|
|
||||||
moduleId?: string | number;
|
|
||||||
description?: string;
|
|
||||||
tags?: string[];
|
|
||||||
grouped?: boolean;
|
|
||||||
environmentId?: string;
|
|
||||||
uploadFileIds?: string[];
|
|
||||||
linkFileIds?: string[];
|
|
||||||
deleteFileIds?: string[];
|
|
||||||
unLinkFileIds?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 场景详情
|
// 场景详情
|
||||||
export interface ApiScenarioTableItem {
|
export interface ApiScenarioTableItem {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -408,3 +391,19 @@ export interface ApiScenarioDebugRequest {
|
||||||
linkFileIds: string[];
|
linkFileIds: string[];
|
||||||
frontendDebug?: boolean;
|
frontendDebug?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 场景修改参数
|
||||||
|
export interface ApiScenarioUpdateDTO extends Partial<Scenario> {
|
||||||
|
id: string | number;
|
||||||
|
name?: string;
|
||||||
|
status?: ApiScenarioStatus;
|
||||||
|
moduleId?: string | number;
|
||||||
|
description?: string;
|
||||||
|
tags?: string[];
|
||||||
|
grouped?: boolean;
|
||||||
|
environmentId?: string;
|
||||||
|
uploadFileIds?: string[];
|
||||||
|
linkFileIds?: string[];
|
||||||
|
deleteFileIds?: string[];
|
||||||
|
unLinkFileIds?: string[];
|
||||||
|
}
|
||||||
|
|
|
@ -217,7 +217,7 @@
|
||||||
<postcondition
|
<postcondition
|
||||||
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
|
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
|
||||||
v-model:config="requestVModel.children[0].postProcessorConfig"
|
v-model:config="requestVModel.children[0].postProcessorConfig"
|
||||||
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
|
:response="props.stepResponses?.[requestVModel.stepId].responseResult.body"
|
||||||
:layout="activeLayout"
|
:layout="activeLayout"
|
||||||
:disabled="!isEditableApi"
|
:disabled="!isEditableApi"
|
||||||
:second-box-height="secondBoxHeight"
|
:second-box-height="secondBoxHeight"
|
||||||
|
@ -227,6 +227,7 @@
|
||||||
<assertion
|
<assertion
|
||||||
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
|
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
|
||||||
v-model:params="requestVModel.children[0].assertionConfig.assertions"
|
v-model:params="requestVModel.children[0].assertionConfig.assertions"
|
||||||
|
:response="props.stepResponses?.[requestVModel.stepId].responseResult.body"
|
||||||
is-definition
|
is-definition
|
||||||
:disabled="!isEditableApi"
|
:disabled="!isEditableApi"
|
||||||
:assertion-config="requestVModel.children[0].assertionConfig"
|
:assertion-config="requestVModel.children[0].assertionConfig"
|
||||||
|
|
|
@ -362,7 +362,7 @@
|
||||||
...props.request,
|
...props.request,
|
||||||
response: {
|
response: {
|
||||||
requestResults: [props.stepResponses?.[props.request?.stepId] || defaultResponse.requestResults[0]],
|
requestResults: [props.stepResponses?.[props.request?.stepId] || defaultResponse.requestResults[0]],
|
||||||
console: props.stepResponses?.[props.request?.stepId].console || '',
|
console: props.stepResponses?.[props.request?.stepId]?.console || '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (isQuote.value || isCopyNeedInit.value) {
|
if (isQuote.value || isCopyNeedInit.value) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MsTag :self-style="status.style" :size="props.size"> {{ status.text }}</MsTag>
|
<MsTag v-if="status" :self-style="status.style" :size="props.size"> {{ status.text }}</MsTag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
import { ScenarioExecuteStatus } from '@/enums/apiEnum';
|
import { ScenarioExecuteStatus } from '@/enums/apiEnum';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
status: ScenarioExecuteStatus;
|
status?: ScenarioExecuteStatus;
|
||||||
size?: Size;
|
size?: Size;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -37,16 +37,23 @@
|
||||||
color: 'rgb(var(--success-6))',
|
color: 'rgb(var(--success-6))',
|
||||||
text: 'common.success',
|
text: 'common.success',
|
||||||
},
|
},
|
||||||
|
[ScenarioExecuteStatus.UN_EXECUTE]: {
|
||||||
|
bgColor: 'var(--color-text-4)',
|
||||||
|
color: 'var(--color-text-n9)',
|
||||||
|
text: 'apiScenario.unExecute',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
const status = computed(() => {
|
const status = computed(() => {
|
||||||
const config = statusMap[props.status];
|
if (props.status) {
|
||||||
return {
|
const config = statusMap[props.status];
|
||||||
style: {
|
return {
|
||||||
backgroundColor: config?.bgColor,
|
style: {
|
||||||
color: config?.color,
|
backgroundColor: config?.bgColor,
|
||||||
},
|
color: config?.color,
|
||||||
text: t(config?.text),
|
},
|
||||||
};
|
text: t(config?.text),
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const defaultLoopController = {
|
||||||
variable: '', // 变量名
|
variable: '', // 变量名
|
||||||
},
|
},
|
||||||
msCountController: {
|
msCountController: {
|
||||||
loops: 0, // 循环次数
|
loops: 1, // 循环次数
|
||||||
loopTime: 0, // 循环间隔时间
|
loopTime: 0, // 循环间隔时间
|
||||||
},
|
},
|
||||||
whileController: {
|
whileController: {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
/>
|
/>
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-if="innerData.loopType === ScenarioStepLoopTypeEnum.LOOP_COUNT"
|
v-if="innerData.loopType === ScenarioStepLoopTypeEnum.LOOP_COUNT"
|
||||||
:content="innerData.msCountController.loops.toString()"
|
:content="innerData.msCountController.loops?.toString()"
|
||||||
:disabled="!innerData.msCountController.loops"
|
:disabled="!innerData.msCountController.loops"
|
||||||
>
|
>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
class="w-[80px] px-[8px]"
|
class="w-[80px] px-[8px]"
|
||||||
size="mini"
|
size="mini"
|
||||||
:step="1"
|
:step="1"
|
||||||
:min="0"
|
:min="1"
|
||||||
hide-button
|
hide-button
|
||||||
:precision="0"
|
:precision="0"
|
||||||
model-event="input"
|
model-event="input"
|
||||||
|
@ -118,7 +118,10 @@
|
||||||
>
|
>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip :content="innerData.whileController.timeout.toString()" :disabled="!innerData.whileController.timeout">
|
<a-tooltip
|
||||||
|
:content="innerData.whileController.timeout?.toString()"
|
||||||
|
:disabled="!innerData.whileController.timeout"
|
||||||
|
>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
v-model:model-value="innerData.whileController.timeout"
|
v-model:model-value="innerData.whileController.timeout"
|
||||||
class="w-[100px] px-[8px]"
|
class="w-[100px] px-[8px]"
|
||||||
|
@ -138,7 +141,7 @@
|
||||||
</template>
|
</template>
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-if="innerData.loopType !== ScenarioStepLoopTypeEnum.WHILE"
|
v-if="innerData.loopType !== ScenarioStepLoopTypeEnum.WHILE"
|
||||||
:content="innerData.forEachController.loopTime.toString()"
|
:content="innerData.forEachController.loopTime?.toString()"
|
||||||
:disabled="!innerData.forEachController.loopTime"
|
:disabled="!innerData.forEachController.loopTime"
|
||||||
>
|
>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center gap-[4px]" draggable="false">
|
<div class="flex items-center gap-[4px]" draggable="false">
|
||||||
<a-tooltip :content="innerData.delay.toString()" :disabled="!innerData.delay">
|
<a-tooltip :content="innerData.delay?.toString()" :disabled="!innerData.delay">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
v-model:model-value="innerData.delay"
|
v-model:model-value="innerData.delay"
|
||||||
class="max-w-[500px] px-[8px]"
|
class="max-w-[500px] px-[8px]"
|
||||||
|
|
|
@ -177,13 +177,15 @@
|
||||||
</template>
|
</template>
|
||||||
<template #extraEnd="step">
|
<template #extraEnd="step">
|
||||||
<a-popover
|
<a-popover
|
||||||
v-if="step.executeStatus && checkStepIsApi(step)"
|
v-if="
|
||||||
|
getExecuteStatus(step) === ScenarioExecuteStatus.SUCCESS ||
|
||||||
|
getExecuteStatus(step) === ScenarioExecuteStatus.FAILED
|
||||||
|
"
|
||||||
position="br"
|
position="br"
|
||||||
content-class="scenario-step-response-popover"
|
content-class="scenario-step-response-popover"
|
||||||
:disabled="![ScenarioExecuteStatus.SUCCESS, ScenarioExecuteStatus.FAILED].includes(step.executeStatus)"
|
|
||||||
@popup-visible-change="handleResponsePopoverVisibleChange($event, step)"
|
@popup-visible-change="handleResponsePopoverVisibleChange($event, step)"
|
||||||
>
|
>
|
||||||
<executeStatus :status="getExecuteStatus(step) || step.executeStatus" size="small" />
|
<executeStatus :status="getExecuteStatus(step)" size="small" />
|
||||||
<template #content>
|
<template #content>
|
||||||
<responseResult
|
<responseResult
|
||||||
:active-tab="ResponseComposition.BODY"
|
:active-tab="ResponseComposition.BODY"
|
||||||
|
@ -204,11 +206,7 @@
|
||||||
</responseResult>
|
</responseResult>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<executeStatus
|
<executeStatus v-else-if="step.executeStatus" :status="getExecuteStatus(step)" size="small" />
|
||||||
v-else-if="step.executeStatus"
|
|
||||||
:status="getExecuteStatus(step) || step.executeStatus"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-if="steps.length === 0 && stepKeyword.trim() !== ''" #empty>
|
<template v-if="steps.length === 0 && stepKeyword.trim() !== ''" #empty>
|
||||||
<div
|
<div
|
||||||
|
@ -793,6 +791,12 @@
|
||||||
scenarioConfig: scenario.value.scenarioConfig,
|
scenarioConfig: scenario.value.scenarioConfig,
|
||||||
frontendDebug: executeType === 'localExec',
|
frontendDebug: executeType === 'localExec',
|
||||||
...executeParams,
|
...executeParams,
|
||||||
|
steps: mapTree(executeParams.steps, (node) => {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
if (executeType === 'localExec' && localExecuteUrl) {
|
if (executeType === 'localExec' && localExecuteUrl) {
|
||||||
await localExecuteApiDebug(localExecuteUrl, res);
|
await localExecuteApiDebug(localExecuteUrl, res);
|
||||||
|
|
|
@ -223,6 +223,12 @@
|
||||||
if (activeScenarioTab.value.isNew) {
|
if (activeScenarioTab.value.isNew) {
|
||||||
const res = await addScenario({
|
const res = await addScenario({
|
||||||
...activeScenarioTab.value,
|
...activeScenarioTab.value,
|
||||||
|
steps: mapTree(activeScenarioTab.value.steps, (node) => {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||||
|
};
|
||||||
|
}),
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
environmentId: currentEnvConfig.value?.id || '',
|
environmentId: currentEnvConfig.value?.id || '',
|
||||||
});
|
});
|
||||||
|
@ -246,6 +252,12 @@
|
||||||
await updateScenario({
|
await updateScenario({
|
||||||
...activeScenarioTab.value,
|
...activeScenarioTab.value,
|
||||||
environmentId: currentEnvConfig.value?.id || '',
|
environmentId: currentEnvConfig.value?.id || '',
|
||||||
|
steps: mapTree(activeScenarioTab.value.steps, (node) => {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Message.success(activeScenarioTab.value.isNew ? t('common.createSuccess') : t('common.saveSuccess'));
|
Message.success(activeScenarioTab.value.isNew ? t('common.createSuccess') : t('common.saveSuccess'));
|
||||||
|
@ -394,6 +406,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (executeType === 'localExec' && localExecuteUrl) {
|
if (executeType === 'localExec' && localExecuteUrl) {
|
||||||
|
// 本地执行需要调 debug 接口获取响应结果,然后再调本地执行接口
|
||||||
await localExecuteApiDebug(localExecuteUrl, res);
|
await localExecuteApiDebug(localExecuteUrl, res);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -137,6 +137,7 @@ export default {
|
||||||
'apiScenario.saveAsApi': '保存为新接口',
|
'apiScenario.saveAsApi': '保存为新接口',
|
||||||
'apiScenario.scenarioLevel': '场景等级',
|
'apiScenario.scenarioLevel': '场景等级',
|
||||||
'apiScenario.running': '执行中',
|
'apiScenario.running': '执行中',
|
||||||
|
'apiScenario.unExecute': '未执行',
|
||||||
'apiScenario.response': '响应内容',
|
'apiScenario.response': '响应内容',
|
||||||
// 执行历史
|
// 执行历史
|
||||||
'apiScenario.executeHistory.searchPlaceholder': '通过ID或名称搜索',
|
'apiScenario.executeHistory.searchPlaceholder': '通过ID或名称搜索',
|
||||||
|
|
Loading…
Reference in New Issue