fix(all): 场景api、case替换&修复部分 bug
This commit is contained in:
parent
6e2dc8068d
commit
6178b24103
|
@ -13,38 +13,38 @@ export default mergeConfig(
|
||||||
},
|
},
|
||||||
proxy: {
|
proxy: {
|
||||||
'/ws': {
|
'/ws': {
|
||||||
target: 'http://172.16.200.18:8081/',
|
target: 'https://qadevtest.fit2cloud.com/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/front\/ws/, ''),
|
rewrite: (path: string) => path.replace(/^\/front\/ws/, ''),
|
||||||
ws: true,
|
ws: true,
|
||||||
},
|
},
|
||||||
'/front': {
|
'/front': {
|
||||||
target: 'http://172.16.200.18:8081/',
|
target: 'https://qadevtest.fit2cloud.com/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/front/, ''),
|
rewrite: (path: string) => path.replace(/^\/front/, ''),
|
||||||
},
|
},
|
||||||
'/file': {
|
'/file': {
|
||||||
target: 'http://172.16.200.18:8081/',
|
target: 'https://qadevtest.fit2cloud.com/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/front\/file/, ''),
|
rewrite: (path: string) => path.replace(/^\/front\/file/, ''),
|
||||||
},
|
},
|
||||||
'/attachment': {
|
'/attachment': {
|
||||||
target: 'http://172.16.200.18:8081/',
|
target: 'https://qadevtest.fit2cloud.com/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/front\/attachment/, ''),
|
rewrite: (path: string) => path.replace(/^\/front\/attachment/, ''),
|
||||||
},
|
},
|
||||||
'/bug/attachment': {
|
'/bug/attachment': {
|
||||||
target: 'http://172.16.200.18:8081/',
|
target: 'https://qadevtest.fit2cloud.com/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/front\/bug\/attachment/, ''),
|
rewrite: (path: string) => path.replace(/^\/front\/bug\/attachment/, ''),
|
||||||
},
|
},
|
||||||
'/plugin/image': {
|
'/plugin/image': {
|
||||||
target: 'http://172.16.200.18:8081/',
|
target: 'https://qadevtest.fit2cloud.com/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/front\/plugin\/image/, ''),
|
rewrite: (path: string) => path.replace(/^\/front\/plugin\/image/, ''),
|
||||||
},
|
},
|
||||||
'/base-display': {
|
'/base-display': {
|
||||||
target: 'http://172.16.200.18:8081/',
|
target: 'https://qadevtest.fit2cloud.com/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/front\/base-display/, ''),
|
rewrite: (path: string) => path.replace(/^\/front\/base-display/, ''),
|
||||||
},
|
},
|
||||||
|
|
|
@ -745,6 +745,9 @@
|
||||||
background-color: rgb(var(--primary-5)) !important;
|
background-color: rgb(var(--primary-5)) !important;
|
||||||
}
|
}
|
||||||
.arco-switch-type-circle.arco-switch-disabled {
|
.arco-switch-type-circle.arco-switch-disabled {
|
||||||
|
background-color: var(--color-text-n8) !important;
|
||||||
|
}
|
||||||
|
.arco-switch-type-circle.arco-switch-checked.arco-switch-disabled {
|
||||||
background-color: rgb(var(--primary-3)) !important;
|
background-color: rgb(var(--primary-3)) !important;
|
||||||
}
|
}
|
||||||
.arco-switch-type-line.arco-switch-small {
|
.arco-switch-type-line.arco-switch-small {
|
||||||
|
@ -774,13 +777,14 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
background: var(--color-text-n9);
|
background: var(--color-text-n9);
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
.ms-pagination-jumper-input {
|
.ms-pagination-jumper-input {
|
||||||
padding: 3px 8px;
|
padding: 3px 8px;
|
||||||
width: 57px;
|
width: 57px;
|
||||||
border: 1px solid var(--color-text-input-border);
|
border: 1px solid var(--color-text-input-border);
|
||||||
border-radius: 3px;
|
border-radius: var(--border-radius-small);
|
||||||
color: var(--color-text-1);
|
color: var(--color-text-1);
|
||||||
background: var(--color-text-10);
|
background: var(--color-text-10);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
|
@ -132,7 +132,7 @@
|
||||||
v-model:data="getCurrentItemState"
|
v-model:data="getCurrentItemState"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
@deleteScriptItem="deleteScriptItem"
|
@delete-script-item="deleteScriptItem"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -157,6 +157,7 @@
|
||||||
import ScriptTab from './comp/ScriptTab.vue';
|
import ScriptTab from './comp/ScriptTab.vue';
|
||||||
import StatusCodeTab from './comp/StatusCodeTab.vue';
|
import StatusCodeTab from './comp/StatusCodeTab.vue';
|
||||||
import VariableTab from './comp/VariableTab.vue';
|
import VariableTab from './comp/VariableTab.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import { characterLimit } from '@/utils';
|
import { characterLimit } from '@/utils';
|
||||||
|
|
|
@ -207,11 +207,11 @@
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
<div
|
<div
|
||||||
v-if="attrs.showFooterActionWrap"
|
v-if="showBatchAction || !!attrs.showPagination"
|
||||||
class="mt-[16px] flex h-[32px] flex-row flex-nowrap items-center"
|
class="mt-[16px] flex h-[32px] flex-row flex-nowrap items-center"
|
||||||
:class="{ 'justify-between': showBatchAction }"
|
:class="{ 'justify-between': showBatchAction }"
|
||||||
>
|
>
|
||||||
<span v-if="!props.actionConfig && selectedCount > 0" class="title text-[var(--color-text-2)]">
|
<span v-if="props.actionConfig && selectedCount > 0" class="title text-[var(--color-text-2)]">
|
||||||
{{ t('msTable.batch.selected', { count: selectedCount }) }}
|
{{ t('msTable.batch.selected', { count: selectedCount }) }}
|
||||||
<a-button class="clear-btn ml-[12px] px-2" type="text" @click="emit('clearSelector')">
|
<a-button class="clear-btn ml-[12px] px-2" type="text" @click="emit('clearSelector')">
|
||||||
{{ t('msTable.batch.clear') }}
|
{{ t('msTable.batch.clear') }}
|
||||||
|
@ -232,7 +232,8 @@
|
||||||
v-if="!!attrs.showPagination"
|
v-if="!!attrs.showPagination"
|
||||||
size="small"
|
size="small"
|
||||||
v-bind="(attrs.msPagination as MsPaginationI)"
|
v-bind="(attrs.msPagination as MsPaginationI)"
|
||||||
:simple="showBatchAction"
|
:simple="!!showBatchAction"
|
||||||
|
:show-jumper="(attrs.msPagination as MsPaginationI).total / (attrs.msPagination as MsPaginationI).pageSize > 5"
|
||||||
@change="pageChange"
|
@change="pageChange"
|
||||||
@page-size-change="pageSizeChange"
|
@page-size-change="pageSizeChange"
|
||||||
/>
|
/>
|
||||||
|
@ -293,7 +294,6 @@
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
selectedKeys: Set<string>;
|
selectedKeys: Set<string>;
|
||||||
selectedKey: string;
|
|
||||||
excludeKeys: Set<string>;
|
excludeKeys: Set<string>;
|
||||||
selectorStatus: SelectAllEnum;
|
selectorStatus: SelectAllEnum;
|
||||||
actionConfig?: BatchActionConfig;
|
actionConfig?: BatchActionConfig;
|
||||||
|
@ -316,7 +316,6 @@
|
||||||
firstColumnWidth?: number; // 选择、拖拽列的宽度
|
firstColumnWidth?: number; // 选择、拖拽列的宽度
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:selectedKey', value: string): void;
|
|
||||||
(e: 'batchAction', value: BatchActionParams, queryParams: BatchActionQueryParams): void;
|
(e: 'batchAction', value: BatchActionParams, queryParams: BatchActionQueryParams): void;
|
||||||
(e: 'pageChange', value: number): void;
|
(e: 'pageChange', value: number): void;
|
||||||
(e: 'pageSizeChange', value: number): void;
|
(e: 'pageSizeChange', value: number): void;
|
||||||
|
@ -413,15 +412,15 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const innerSelectedKey = defineModel<string>('selectedKey', { default: '' }); // 内部维护的单选选中项
|
const selectedKey = defineModel<string>('selectedKey', { default: '' }); // 内部维护的单选选中项
|
||||||
const tempRecord = ref<TableData>({});
|
const tempRecord = ref<TableData>({});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => attrs.data,
|
() => attrs.data,
|
||||||
(arr) => {
|
(arr) => {
|
||||||
if (innerSelectedKey.value && Array.isArray(arr) && arr.length > 0) {
|
if (selectedKey.value && Array.isArray(arr) && arr.length > 0) {
|
||||||
arr = arr.map((item: TableData) => {
|
arr = arr.map((item: TableData) => {
|
||||||
if (item.id === innerSelectedKey.value) {
|
if (item.id === selectedKey.value) {
|
||||||
item.tableChecked = true;
|
item.tableChecked = true;
|
||||||
tempRecord.value = item;
|
tempRecord.value = item;
|
||||||
}
|
}
|
||||||
|
@ -436,7 +435,7 @@
|
||||||
|
|
||||||
function handleRadioChange(val: boolean, record: TableData) {
|
function handleRadioChange(val: boolean, record: TableData) {
|
||||||
if (val) {
|
if (val) {
|
||||||
innerSelectedKey.value = record.id;
|
selectedKey.value = record.id;
|
||||||
record.tableChecked = true;
|
record.tableChecked = true;
|
||||||
tempRecord.value.tableChecked = false;
|
tempRecord.value.tableChecked = false;
|
||||||
tempRecord.value = record;
|
tempRecord.value = record;
|
||||||
|
@ -469,7 +468,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const showBatchAction = computed(() => {
|
const showBatchAction = computed(() => {
|
||||||
return selectedCount.value > 0 && !!attrs.selectable;
|
return selectedCount.value > 0 && !!attrs.selectable && props.actionConfig;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleBatchAction = (value: BatchActionParams) => {
|
const handleBatchAction = (value: BatchActionParams) => {
|
||||||
|
|
|
@ -79,7 +79,6 @@ export default function useTableProps<T>(
|
||||||
emptyDataShowLine: true, // 空数据是否显示 "-"
|
emptyDataShowLine: true, // 空数据是否显示 "-"
|
||||||
/** Column Selector */
|
/** Column Selector */
|
||||||
showJumpMethod: false, // 是否显示跳转方法
|
showJumpMethod: false, // 是否显示跳转方法
|
||||||
showFooterActionWrap: false, // 是否显示底部操作区域
|
|
||||||
isSimpleSetting: false, // 是否是简易column设置
|
isSimpleSetting: false, // 是否是简易column设置
|
||||||
filterIconAlignLeft: true, // 筛选图标是否靠左
|
filterIconAlignLeft: true, // 筛选图标是否靠左
|
||||||
...props,
|
...props,
|
||||||
|
@ -446,22 +445,9 @@ export default function useTableProps<T>(
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const { heightUsed, showPagination, selectedKeys, msPagination } = propsRes.value;
|
const { heightUsed } = propsRes.value;
|
||||||
let hasFooterAction = false;
|
|
||||||
if (showPagination) {
|
|
||||||
const { pageSize, total } = msPagination as Pagination;
|
|
||||||
/*
|
|
||||||
* 是否有底部操作栏 包括 批量操作 和 分页器
|
|
||||||
* 1. 有分页器,且总条数大于每页条数
|
|
||||||
* 2. 有选中项
|
|
||||||
*/
|
|
||||||
hasFooterAction = total > pageSize || selectedKeys.size > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
propsRes.value.showFooterActionWrap = hasFooterAction;
|
|
||||||
if (props?.heightUsed) {
|
if (props?.heightUsed) {
|
||||||
const currentY =
|
const currentY = appStore.innerHeight - (heightUsed || defaultHeightUsed);
|
||||||
appStore.innerHeight - (heightUsed || defaultHeightUsed) + (hasFooterAction ? 0 : footerActionWrapHeight);
|
|
||||||
propsRes.value.scroll = { ...propsRes.value.scroll, y: currentY };
|
propsRes.value.scroll = { ...propsRes.value.scroll, y: currentY };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -249,6 +249,7 @@ export enum ScenarioExecuteStatus {
|
||||||
FAILED = 'FAILED',
|
FAILED = 'FAILED',
|
||||||
STOP = 'STOP',
|
STOP = 'STOP',
|
||||||
UN_EXECUTE = 'UN_EXECUTE',
|
UN_EXECUTE = 'UN_EXECUTE',
|
||||||
|
FAKE_ERROR = 'FAKE_ERROR',
|
||||||
}
|
}
|
||||||
// 场景步骤类型
|
// 场景步骤类型
|
||||||
export enum ScenarioStepType {
|
export enum ScenarioStepType {
|
||||||
|
|
|
@ -148,7 +148,7 @@ export default {
|
||||||
'common.value.notNull': '属性值不能为空',
|
'common.value.notNull': '属性值不能为空',
|
||||||
'common.nameNotNull': '名称不能为空',
|
'common.nameNotNull': '名称不能为空',
|
||||||
'common.namePlaceholder': '请输入名称,按回车键保存',
|
'common.namePlaceholder': '请输入名称,按回车键保存',
|
||||||
'common.unsavedLeave': '有标签页的内容未保存,离开后后未保存的内容将丢失,确定要离开吗?',
|
'common.unsavedLeave': '有标签页的内容未保存,离开后未保存的内容将丢失,确定要离开吗?',
|
||||||
'common.image': '图片',
|
'common.image': '图片',
|
||||||
'common.text': '文本',
|
'common.text': '文本',
|
||||||
};
|
};
|
||||||
|
|
|
@ -402,6 +402,7 @@ export interface Scenario {
|
||||||
executeTime?: string | number; // 执行时间
|
executeTime?: string | number; // 执行时间
|
||||||
executeSuccessCount: number; // 执行成功数量
|
executeSuccessCount: number; // 执行成功数量
|
||||||
executeFailCount: number; // 执行失败数量
|
executeFailCount: number; // 执行失败数量
|
||||||
|
executeFakeErrorCount: number; // 执行误报数量
|
||||||
reportId: string | number; // 场景报告 id
|
reportId: string | number; // 场景报告 id
|
||||||
stepResponses: Record<string | number, Array<RequestResult>>; // 步骤响应集合,key 为步骤 id,value 为步骤响应内容
|
stepResponses: Record<string | number, Array<RequestResult>>; // 步骤响应集合,key 为步骤 id,value 为步骤响应内容
|
||||||
isExecute?: boolean; // 是否从列表执行进去场景详情
|
isExecute?: boolean; // 是否从列表执行进去场景详情
|
||||||
|
|
|
@ -261,7 +261,7 @@ export function mapTree<T>(
|
||||||
if (newNode) {
|
if (newNode) {
|
||||||
newNode.level = _level;
|
newNode.level = _level;
|
||||||
if (newNode[customChildrenKey] && newNode[customChildrenKey].length > 0) {
|
if (newNode[customChildrenKey] && newNode[customChildrenKey].length > 0) {
|
||||||
newNode[customChildrenKey] = mapFunc(newNode[customChildrenKey], fullPath, _level + 1, node);
|
newNode[customChildrenKey] = mapFunc(newNode[customChildrenKey], fullPath, _level + 1, newNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newNode;
|
return newNode;
|
||||||
|
|
|
@ -61,9 +61,11 @@
|
||||||
value: item.id,
|
value: item.id,
|
||||||
}));
|
}));
|
||||||
currentEnv.value = currentEnv.value.length ? currentEnv.value : res[0]?.id;
|
currentEnv.value = currentEnv.value.length ? currentEnv.value : res[0]?.id;
|
||||||
|
nextTick(() => {
|
||||||
if (currentEnv.value) {
|
if (currentEnv.value) {
|
||||||
await initEnvironment();
|
initEnvironment();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -82,6 +84,18 @@
|
||||||
openNewPage(ProjectManagementRouteEnum.PROJECT_MANAGEMENT_ENVIRONMENT_MANAGEMENT);
|
openNewPage(ProjectManagementRouteEnum.PROJECT_MANAGEMENT_ENVIRONMENT_MANAGEMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => currentEnv.value,
|
||||||
|
(val) => {
|
||||||
|
if (!val) {
|
||||||
|
currentEnv.value = (envOptions.value[0]?.value as string) || '';
|
||||||
|
nextTick(() => {
|
||||||
|
initEnvironment();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
initEnvList();
|
initEnvList();
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<MsCodeEditor
|
<MsCodeEditor
|
||||||
v-show="!showImg || showType === 'text'"
|
v-show="!showImg || showType === 'text'"
|
||||||
ref="responseEditorRef"
|
ref="responseEditorRef"
|
||||||
:model-value="props.requestResult?.responseResult.body"
|
:model-value="props.requestResult?.responseResult.body || ''"
|
||||||
:language="responseLanguage"
|
:language="responseLanguage"
|
||||||
theme="vs"
|
theme="vs"
|
||||||
:height="showImg ? 'calc(100% - 26px)' : '100%'"
|
:height="showImg ? 'calc(100% - 26px)' : '100%'"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<MsCodeEditor
|
<MsCodeEditor
|
||||||
:model-value="props.console?.trim()"
|
:model-value="props.console?.trim() || ''"
|
||||||
:language="LanguageEnum.PLAINTEXT"
|
:language="LanguageEnum.PLAINTEXT"
|
||||||
theme="MS-text"
|
theme="MS-text"
|
||||||
height="100%"
|
height="100%"
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
|
||||||
import CaseReportCom from './caseReportCom.vue';
|
import CaseReportCom from './caseReportCom.vue';
|
||||||
|
|
||||||
import { getShareInfo, getShareTime, reportCaseDetail } from '@/api/modules/api-test/report';
|
import { getShareInfo, reportCaseDetail } from '@/api/modules/api-test/report';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,23 @@
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
props.step &&
|
||||||
|
!props.step.isQuoteScenarioStep &&
|
||||||
|
props.step.resourceId &&
|
||||||
|
props.step?.stepType !== ScenarioStepType.CUSTOM_REQUEST
|
||||||
|
"
|
||||||
|
class="ml-auto"
|
||||||
|
>
|
||||||
|
<replaceButton
|
||||||
|
:steps="props.steps"
|
||||||
|
:step="props.step"
|
||||||
|
:resource-id="props.step.resourceId"
|
||||||
|
:scenario-id="scenarioId"
|
||||||
|
@replace="handleReplace"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
||||||
class="customApiDrawer-title-right ml-auto flex items-center gap-[16px]"
|
class="customApiDrawer-title-right ml-auto flex items-center gap-[16px]"
|
||||||
|
@ -306,6 +323,7 @@
|
||||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||||
import assertion from '@/components/business/ms-assertion/index.vue';
|
import assertion from '@/components/business/ms-assertion/index.vue';
|
||||||
import loopPagination from './loopPagination.vue';
|
import loopPagination from './loopPagination.vue';
|
||||||
|
import replaceButton from './replaceButton.vue';
|
||||||
import stepTypeVue from './stepType/stepType.vue';
|
import stepTypeVue from './stepType/stepType.vue';
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
|
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
|
||||||
|
@ -391,6 +409,7 @@
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
request?: RequestParam; // 请求参数集合
|
request?: RequestParam; // 请求参数集合
|
||||||
step?: ScenarioStepItem;
|
step?: ScenarioStepItem;
|
||||||
|
steps: ScenarioStepItem[];
|
||||||
detailLoading?: boolean; // 详情加载状态
|
detailLoading?: boolean; // 详情加载状态
|
||||||
permissionMap?: {
|
permissionMap?: {
|
||||||
execute: string;
|
execute: string;
|
||||||
|
@ -404,6 +423,7 @@
|
||||||
(e: 'applyStep', request: RequestParam): void;
|
(e: 'applyStep', request: RequestParam): void;
|
||||||
(e: 'execute', request: RequestParam, executeType?: 'localExec' | 'serverExec'): void;
|
(e: 'execute', request: RequestParam, executeType?: 'localExec' | 'serverExec'): void;
|
||||||
(e: 'stopDebug'): void;
|
(e: 'stopDebug'): void;
|
||||||
|
(e: 'replace', newStep: ScenarioStepItem): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
@ -492,7 +512,11 @@
|
||||||
});
|
});
|
||||||
// 抽屉标题
|
// 抽屉标题
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
if (_stepType.value.isCopyApi || _stepType.value.isQuoteApi) {
|
if (
|
||||||
|
_stepType.value.isCopyApi ||
|
||||||
|
_stepType.value.isQuoteApi ||
|
||||||
|
props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST
|
||||||
|
) {
|
||||||
return props.step?.name;
|
return props.step?.name;
|
||||||
}
|
}
|
||||||
return t('apiScenario.customApi');
|
return t('apiScenario.customApi');
|
||||||
|
@ -1014,6 +1038,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopDebug() {
|
function stopDebug() {
|
||||||
|
requestVModel.value.executeLoading = false;
|
||||||
emit('stopDebug');
|
emit('stopDebug');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1097,6 +1122,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换步骤
|
||||||
|
* @param newStep 替换的新步骤
|
||||||
|
*/
|
||||||
|
function handleReplace(newStep: ScenarioStepItem) {
|
||||||
|
emit('replace', newStep);
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => visible.value,
|
() => visible.value,
|
||||||
async (val) => {
|
async (val) => {
|
||||||
|
|
|
@ -28,11 +28,17 @@
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<MsIcon type="icon-icon_edit_outlined" class="edit-script-name-icon" @click="showEditScriptNameInput" />
|
<MsIcon type="icon-icon_edit_outlined" class="edit-script-name-icon" @click="showEditScriptNameInput" />
|
||||||
</div>
|
</div>
|
||||||
<div class="right-operation-button-icon flex items-center">
|
<div
|
||||||
<MsButton type="icon" status="secondary">
|
v-if="activeStep && !activeStep.isQuoteScenarioStep && requestVModel.resourceId"
|
||||||
<MsIcon type="icon-icon_swich" />
|
class="right-operation-button-icon flex items-center"
|
||||||
{{ t('common.replace') }}
|
>
|
||||||
</MsButton>
|
<replaceButton
|
||||||
|
:steps="props.steps"
|
||||||
|
:step="activeStep"
|
||||||
|
:resource-id="requestVModel.resourceId"
|
||||||
|
:scenario-id="scenarioId"
|
||||||
|
@replace="handleReplace"
|
||||||
|
/>
|
||||||
<MsButton class="mr-4" type="icon" status="secondary" @click="handleDelete">
|
<MsButton class="mr-4" type="icon" status="secondary" @click="handleDelete">
|
||||||
<MsIcon type="icon-icon_delete-trash_outlined" />
|
<MsIcon type="icon-icon_delete-trash_outlined" />
|
||||||
{{ t('common.delete') }}
|
{{ t('common.delete') }}
|
||||||
|
@ -255,6 +261,7 @@
|
||||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||||
import assertion from '@/components/business/ms-assertion/index.vue';
|
import assertion from '@/components/business/ms-assertion/index.vue';
|
||||||
import loopPagination from './loopPagination.vue';
|
import loopPagination from './loopPagination.vue';
|
||||||
|
import replaceButton from './replaceButton.vue';
|
||||||
import stepType from './stepType/stepType.vue';
|
import stepType from './stepType/stepType.vue';
|
||||||
import auth from '@/views/api-test/components/requestComposition/auth.vue';
|
import auth from '@/views/api-test/components/requestComposition/auth.vue';
|
||||||
import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue';
|
import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue';
|
||||||
|
@ -306,6 +313,7 @@
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
request?: RequestParam; // 请求参数集合
|
request?: RequestParam; // 请求参数集合
|
||||||
|
steps: ScenarioStepItem[];
|
||||||
stepResponses?: Record<string | number, RequestResult[]>;
|
stepResponses?: Record<string | number, RequestResult[]>;
|
||||||
fileParams?: ScenarioStepFileParams;
|
fileParams?: ScenarioStepFileParams;
|
||||||
permissionMap?: {
|
permissionMap?: {
|
||||||
|
@ -317,6 +325,7 @@
|
||||||
(e: 'deleteStep'): void;
|
(e: 'deleteStep'): void;
|
||||||
(e: 'execute', request: RequestParam, executeType?: 'localExec' | 'serverExec'): void;
|
(e: 'execute', request: RequestParam, executeType?: 'localExec' | 'serverExec'): void;
|
||||||
(e: 'stopDebug'): void;
|
(e: 'stopDebug'): void;
|
||||||
|
(e: 'replace', newStep: ScenarioStepItem): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
@ -903,6 +912,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopDebug() {
|
function stopDebug() {
|
||||||
|
requestVModel.value.executeLoading = false;
|
||||||
emit('stopDebug');
|
emit('stopDebug');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -956,6 +966,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换步骤
|
||||||
|
* @param newStep 替换的新步骤
|
||||||
|
*/
|
||||||
|
function handleReplace(newStep: ScenarioStepItem) {
|
||||||
|
emit('replace', newStep);
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => visible.value,
|
() => visible.value,
|
||||||
async (val) => {
|
async (val) => {
|
||||||
|
@ -968,6 +986,7 @@
|
||||||
...defaultApiParams,
|
...defaultApiParams,
|
||||||
...props.request,
|
...props.request,
|
||||||
isNew: false,
|
isNew: false,
|
||||||
|
stepId: activeStep.value?.uniqueId || '',
|
||||||
});
|
});
|
||||||
if (isQuote.value || isCopyNeedInit.value) {
|
if (isQuote.value || isCopyNeedInit.value) {
|
||||||
// 引用时,需要初始化引用的详情;复制只在第一次初始化的时候需要加载后台数据(request.request是复制请求时列表参数字段request会为 null,以此判断释放第一次初始化)
|
// 引用时,需要初始化引用的详情;复制只在第一次初始化的时候需要加载后台数据(request.request是复制请求时列表参数字段request会为 null,以此判断释放第一次初始化)
|
||||||
|
|
|
@ -38,6 +38,11 @@
|
||||||
color: 'rgb(var(--danger-6))',
|
color: 'rgb(var(--danger-6))',
|
||||||
text: 'common.fail',
|
text: 'common.fail',
|
||||||
},
|
},
|
||||||
|
[ScenarioExecuteStatus.FAKE_ERROR]: {
|
||||||
|
bgColor: 'rgb(var(--warning-2))',
|
||||||
|
color: 'rgb(var(--warning-5))',
|
||||||
|
text: 'report.fake.error',
|
||||||
|
},
|
||||||
[ScenarioExecuteStatus.SUCCESS]: {
|
[ScenarioExecuteStatus.SUCCESS]: {
|
||||||
bgColor: 'rgb(var(--success-2))',
|
bgColor: 'rgb(var(--success-2))',
|
||||||
color: 'rgb(var(--success-6))',
|
color: 'rgb(var(--success-6))',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MsDrawer
|
<MsDrawer
|
||||||
v-model:visible="visible"
|
v-model:visible="visible"
|
||||||
:title="t('apiScenario.importSystemApi')"
|
:title="props.singleSelect ? t('common.replace') : t('apiScenario.importSystemApi')"
|
||||||
:width="1200"
|
:width="1200"
|
||||||
no-content-padding
|
no-content-padding
|
||||||
disabled-width-drag
|
disabled-width-drag
|
||||||
|
@ -47,6 +47,9 @@
|
||||||
:selected-cases="selectedCases"
|
:selected-cases="selectedCases"
|
||||||
:selected-scenarios="selectedScenarios"
|
:selected-scenarios="selectedScenarios"
|
||||||
:scenario-id="props.scenarioId"
|
:scenario-id="props.scenarioId"
|
||||||
|
:case-id="props.caseId"
|
||||||
|
:api-id="props.apiId"
|
||||||
|
:single-select="props.singleSelect"
|
||||||
@select="handleTableSelect"
|
@select="handleTableSelect"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -67,7 +70,12 @@
|
||||||
<div class="second-text">{{ t('apiScenario.scenario') }}</div>
|
<div class="second-text">{{ t('apiScenario.scenario') }}</div>
|
||||||
<div class="main-text">{{ selectedScenarios.length }}</div>
|
<div class="main-text">{{ selectedScenarios.length }}</div>
|
||||||
<a-divider v-show="totalSelected > 0" direction="vertical" :margin="4"></a-divider>
|
<a-divider v-show="totalSelected > 0" direction="vertical" :margin="4"></a-divider>
|
||||||
<MsButton v-show="totalSelected > 0" type="text" class="!mr-0 ml-[4px]" @click="clearAll">
|
<MsButton
|
||||||
|
v-show="totalSelected > 0 && !props.singleSelect"
|
||||||
|
type="text"
|
||||||
|
class="!mr-0 ml-[4px]"
|
||||||
|
@click="clearAll"
|
||||||
|
>
|
||||||
{{ t('common.clear') }}
|
{{ t('common.clear') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -115,6 +123,9 @@
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
scenarioId?: string | number;
|
scenarioId?: string | number;
|
||||||
|
caseId?: string | number;
|
||||||
|
apiId?: string | number;
|
||||||
|
singleSelect?: boolean;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'copy', data: ImportData): void;
|
(e: 'copy', data: ImportData): void;
|
||||||
|
@ -138,6 +149,11 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleTableSelect(data: MsTableDataItem<ApiCaseDetail | ApiDefinitionDetail | ApiScenarioTableItem>[]) {
|
function handleTableSelect(data: MsTableDataItem<ApiCaseDetail | ApiDefinitionDetail | ApiScenarioTableItem>[]) {
|
||||||
|
if (props.singleSelect) {
|
||||||
|
selectedApis.value = [];
|
||||||
|
selectedCases.value = [];
|
||||||
|
selectedScenarios.value = [];
|
||||||
|
}
|
||||||
if (activeKey.value === 'api') {
|
if (activeKey.value === 'api') {
|
||||||
selectedApis.value = data as MsTableDataItem<ApiDefinitionDetail>[];
|
selectedApis.value = data as MsTableDataItem<ApiDefinitionDetail>[];
|
||||||
} else if (activeKey.value === 'case') {
|
} else if (activeKey.value === 'case') {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
<ms-base-table
|
<ms-base-table
|
||||||
v-bind="currentTable.propsRes.value"
|
v-bind="currentTable.propsRes.value"
|
||||||
|
v-model:selected-key="selectedKey"
|
||||||
no-disable
|
no-disable
|
||||||
filter-icon-align-left
|
filter-icon-align-left
|
||||||
v-on="currentTable.propsEvent.value"
|
v-on="currentTable.propsEvent.value"
|
||||||
|
@ -87,7 +88,7 @@
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import { MsTableColumn, MsTableDataItem } from '@/components/pure/ms-table/type';
|
import { MsTableDataItem } from '@/components/pure/ms-table/type';
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
import { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
import { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
|
@ -114,6 +115,9 @@
|
||||||
selectedCases: MsTableDataItem<ApiCaseDetail>[]; // 已选中的用例
|
selectedCases: MsTableDataItem<ApiCaseDetail>[]; // 已选中的用例
|
||||||
selectedScenarios: MsTableDataItem<ApiScenarioTableItem>[]; // 已选中的场景
|
selectedScenarios: MsTableDataItem<ApiScenarioTableItem>[]; // 已选中的场景
|
||||||
scenarioId?: string | number;
|
scenarioId?: string | number;
|
||||||
|
caseId?: string | number;
|
||||||
|
apiId?: string | number;
|
||||||
|
singleSelect?: boolean; // 是否单选
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'select', data: MsTableDataItem<ApiCaseDetail | ApiDefinitionDetail | ApiScenarioTableItem>[]): void;
|
(e: 'select', data: MsTableDataItem<ApiCaseDetail | ApiDefinitionDetail | ApiScenarioTableItem>[]): void;
|
||||||
|
@ -129,6 +133,7 @@
|
||||||
selectable: true,
|
selectable: true,
|
||||||
showSelectorAll: false,
|
showSelectorAll: false,
|
||||||
heightUsed: 300,
|
heightUsed: 300,
|
||||||
|
selectorType: props.singleSelect ? 'radio' : ('checkbox' as 'checkbox' | 'radio' | 'none' | undefined),
|
||||||
};
|
};
|
||||||
// 接口定义表格
|
// 接口定义表格
|
||||||
const useApiTable = useTable(getDefinitionPage, {
|
const useApiTable = useTable(getDefinitionPage, {
|
||||||
|
@ -357,6 +362,17 @@
|
||||||
emit('select', tableSelectedData.value);
|
emit('select', tableSelectedData.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedKey = ref('');
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => selectedKey.value,
|
||||||
|
(val) => {
|
||||||
|
const selectedData = currentTable.value.propsRes.value.data.find((e: any) => e.id === val);
|
||||||
|
tableSelectedData.value = selectedData ? [selectedData] : [];
|
||||||
|
emit('select', tableSelectedData.value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
function clearSelector() {
|
function clearSelector() {
|
||||||
tableSelectedData.value = [];
|
tableSelectedData.value = [];
|
||||||
currentTable.value.clearSelector();
|
currentTable.value.clearSelector();
|
||||||
|
@ -399,7 +415,7 @@
|
||||||
status: statusFilters.value,
|
status: statusFilters.value,
|
||||||
method: methodFilters.value,
|
method: methodFilters.value,
|
||||||
},
|
},
|
||||||
excludeIds: [props.scenarioId || ''],
|
excludeIds: [props.scenarioId || '', props.caseId || '', props.apiId || ''],
|
||||||
});
|
});
|
||||||
currentTable.value.loadList();
|
currentTable.value.loadList();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
<template>
|
||||||
|
<MsButton type="icon" status="secondary" @click="importApiDrawerVisible = true">
|
||||||
|
<MsIcon type="icon-icon_swich" class="mr-[8px]" />
|
||||||
|
{{ t('common.replace') }}
|
||||||
|
</MsButton>
|
||||||
|
<importApiDrawer
|
||||||
|
v-if="importApiDrawerVisible"
|
||||||
|
v-model:visible="importApiDrawerVisible"
|
||||||
|
:scenario-id="props.scenarioId"
|
||||||
|
:case-id="props.resourceId"
|
||||||
|
:api-id="props.resourceId"
|
||||||
|
single-select
|
||||||
|
@copy="handleImportApiApply('copy', $event)"
|
||||||
|
@quote="handleImportApiApply('quote', $event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
import { ImportData } from './importApiDrawer/index.vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
|
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
|
import { ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
import useCreateActions from '../step/createAction/useCreateActions';
|
||||||
|
|
||||||
|
const importApiDrawer = defineAsyncComponent(() => import('./importApiDrawer/index.vue'));
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
steps: ScenarioStepItem[];
|
||||||
|
step: ScenarioStepItem;
|
||||||
|
resourceId: string | number;
|
||||||
|
scenarioId?: string | number;
|
||||||
|
}>();
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'replace', replaceItem: ScenarioStepItem): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { buildInsertStepInfos } = useCreateActions();
|
||||||
|
|
||||||
|
const importApiDrawerVisible = ref(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理导入系统请求
|
||||||
|
* @param type 导入类型
|
||||||
|
* @param data 导入数据
|
||||||
|
*/
|
||||||
|
function handleImportApiApply(type: 'copy' | 'quote', data: ImportData) {
|
||||||
|
const refType = type === 'copy' ? ScenarioStepRefType.COPY : ScenarioStepRefType.REF;
|
||||||
|
let replaceItem: ScenarioStepItem[];
|
||||||
|
if (data.api.length > 0) {
|
||||||
|
replaceItem = buildInsertStepInfos(
|
||||||
|
data.api,
|
||||||
|
ScenarioStepType.API,
|
||||||
|
refType,
|
||||||
|
props.step.sort,
|
||||||
|
props.step.projectId || appStore.currentProjectId
|
||||||
|
);
|
||||||
|
} else if (data.case.length > 0) {
|
||||||
|
replaceItem = buildInsertStepInfos(
|
||||||
|
data.case,
|
||||||
|
ScenarioStepType.API_CASE,
|
||||||
|
refType,
|
||||||
|
props.step.sort,
|
||||||
|
props.step.projectId || appStore.currentProjectId
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
replaceItem = buildInsertStepInfos(
|
||||||
|
data.scenario,
|
||||||
|
ScenarioStepType.API_SCENARIO,
|
||||||
|
refType,
|
||||||
|
props.step.sort,
|
||||||
|
props.step.projectId || appStore.currentProjectId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
emit('replace', replaceItem[0]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -1,10 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<a-popover
|
<a-popover
|
||||||
|
v-if="
|
||||||
|
[
|
||||||
|
ScenarioStepType.API,
|
||||||
|
ScenarioStepType.API_CASE,
|
||||||
|
ScenarioStepType.SCRIPT,
|
||||||
|
ScenarioStepType.CUSTOM_REQUEST,
|
||||||
|
].includes(step.stepType) &&
|
||||||
|
props.finalExecuteStatus &&
|
||||||
|
[ScenarioExecuteStatus.SUCCESS, ScenarioExecuteStatus.FAILED, ScenarioExecuteStatus.FAKE_ERROR].includes(
|
||||||
|
props.finalExecuteStatus
|
||||||
|
)
|
||||||
|
"
|
||||||
position="lt"
|
position="lt"
|
||||||
content-class="scenario-step-response-popover"
|
content-class="scenario-step-response-popover"
|
||||||
@popup-visible-change="emit('visibleChange', $event, props.step)"
|
@popup-visible-change="emit('visibleChange', $event, props.step)"
|
||||||
>
|
>
|
||||||
<executeStatus :status="finalExecuteStatus" size="small" class="ml-[4px]" />
|
<executeStatus :status="props.finalExecuteStatus" size="small" class="ml-[4px]" />
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex h-full flex-col">
|
<div class="flex h-full flex-col">
|
||||||
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
|
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
|
||||||
|
@ -40,6 +52,13 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
|
<executeStatus
|
||||||
|
v-else-if="step.executeStatus"
|
||||||
|
:status="props.finalExecuteStatus"
|
||||||
|
:extra-text="getExecuteStatusExtraText(step)"
|
||||||
|
size="small"
|
||||||
|
class="ml-[4px]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -50,7 +69,12 @@
|
||||||
|
|
||||||
import { RequestResult } from '@/models/apiTest/common';
|
import { RequestResult } from '@/models/apiTest/common';
|
||||||
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
import { ResponseComposition, ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
import {
|
||||||
|
ResponseComposition,
|
||||||
|
ScenarioExecuteStatus,
|
||||||
|
ScenarioStepLoopTypeEnum,
|
||||||
|
ScenarioStepType,
|
||||||
|
} from '@/enums/apiEnum';
|
||||||
|
|
||||||
const responseResult = defineAsyncComponent(
|
const responseResult = defineAsyncComponent(
|
||||||
() => import('@/views/api-test/components/requestComposition/response/index.vue')
|
() => import('@/views/api-test/components/requestComposition/response/index.vue')
|
||||||
|
@ -59,6 +83,7 @@
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
step: ScenarioStepItem;
|
step: ScenarioStepItem;
|
||||||
stepResponses: Record<string | number, Array<RequestResult>>;
|
stepResponses: Record<string | number, Array<RequestResult>>;
|
||||||
|
finalExecuteStatus?: ScenarioExecuteStatus;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits(['visibleChange']);
|
const emit = defineEmits(['visibleChange']);
|
||||||
|
|
||||||
|
@ -67,15 +92,27 @@
|
||||||
const currentLoop = ref(1);
|
const currentLoop = ref(1);
|
||||||
const currentResponse = computed(() => props.stepResponses?.[props.step.uniqueId]?.[currentLoop.value - 1]);
|
const currentResponse = computed(() => props.stepResponses?.[props.step.uniqueId]?.[currentLoop.value - 1]);
|
||||||
const loopTotal = computed(() => props.stepResponses?.[props.step.uniqueId]?.length || 0);
|
const loopTotal = computed(() => props.stepResponses?.[props.step.uniqueId]?.length || 0);
|
||||||
const finalExecuteStatus = computed(() => {
|
|
||||||
if (props.stepResponses[props.step.uniqueId] && props.stepResponses[props.step.uniqueId].length > 0) {
|
function getExecuteStatusExtraText(step: ScenarioStepItem) {
|
||||||
// 有一次失败就是失败
|
if (
|
||||||
return props.stepResponses[props.step.uniqueId].some((report) => !report.isSuccessful)
|
step.stepType === ScenarioStepType.LOOP_CONTROLLER &&
|
||||||
? ScenarioExecuteStatus.FAILED
|
step.config.loopType === ScenarioStepLoopTypeEnum.LOOP_COUNT &&
|
||||||
: ScenarioExecuteStatus.SUCCESS;
|
step.config.msCountController &&
|
||||||
}
|
step.config.msCountController.loops > 0
|
||||||
return props.step.executeStatus;
|
) {
|
||||||
|
// 循环控制器展示当前执行次数/总次数
|
||||||
|
const firstHasResultChild = step.children?.find((child) => {
|
||||||
|
return (
|
||||||
|
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(step.stepType) ||
|
||||||
|
child.stepType === ScenarioStepType.SCRIPT
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
return firstHasResultChild && props.stepResponses[firstHasResultChild.uniqueId]
|
||||||
|
? `${props.stepResponses[firstHasResultChild.uniqueId].length}/${step.config.msCountController.loops}`
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|
|
@ -37,10 +37,10 @@
|
||||||
<a-button type="secondary" @click="handleDrawerCancel">
|
<a-button type="secondary" @click="handleDrawerCancel">
|
||||||
{{ t('common.cancel') }}
|
{{ t('common.cancel') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="secondary" @click="saveAndContinue">
|
<a-button type="secondary" :disabled="!scriptName" @click="saveAndContinue">
|
||||||
{{ t('common.saveAndContinue') }}
|
{{ t('common.saveAndContinue') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="primary" @click="save">
|
<a-button type="primary" :disabled="!scriptName" @click="save">
|
||||||
{{ t('common.add') }}
|
{{ t('common.add') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const defaultLoopController = {
|
||||||
},
|
},
|
||||||
whileController: {
|
whileController: {
|
||||||
conditionType: WhileConditionType.CONDITION, // 条件类型
|
conditionType: WhileConditionType.CONDITION, // 条件类型
|
||||||
timeout: 0, // 超时时间
|
timeout: 3000, // 超时时间
|
||||||
msWhileScript: {
|
msWhileScript: {
|
||||||
scriptValue: '', // 脚本值
|
scriptValue: '', // 脚本值
|
||||||
}, // 脚本
|
}, // 脚本
|
||||||
|
@ -118,6 +118,7 @@ export const defaultScenario: Scenario = {
|
||||||
executeTime: 0,
|
executeTime: 0,
|
||||||
executeSuccessCount: 0,
|
executeSuccessCount: 0,
|
||||||
executeFailCount: 0,
|
executeFailCount: 0,
|
||||||
|
executeFakeErrorCount: 0,
|
||||||
uploadFileIds: [],
|
uploadFileIds: [],
|
||||||
linkFileIds: [],
|
linkFileIds: [],
|
||||||
reportId: '',
|
reportId: '',
|
||||||
|
|
|
@ -133,8 +133,8 @@ export default function useCreateActions() {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...cloneDeep(defaultStepItemCommon),
|
...cloneDeep(defaultStepItemCommon),
|
||||||
id,
|
id: item.uniqueId || id,
|
||||||
uniqueId: getGenerateId(), // 生成唯一 ID,避免重复引用的步骤无法读取正确的执行结果
|
uniqueId: item.uniqueId || id, // 生成唯一 ID,避免重复引用的步骤无法读取正确的执行结果
|
||||||
config: {
|
config: {
|
||||||
...defaultStepItemCommon.config,
|
...defaultStepItemCommon.config,
|
||||||
...config,
|
...config,
|
||||||
|
|
|
@ -58,7 +58,11 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-[4px]">
|
<div class="flex items-center gap-[4px]">
|
||||||
<div class="text-[var(--color-text-1)]">{{ t('common.fail') }}</div>
|
<div class="text-[var(--color-text-1)]">{{ t('common.fail') }}</div>
|
||||||
<div class="text-[rgb(var(--success-6))]">{{ scenario.executeFailCount }}</div>
|
<div class="text-[rgb(var(--danger-6))]">{{ scenario.executeFailCount }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-[4px]">
|
||||||
|
<div class="text-[var(--color-text-1)]">{{ t('report.fake.error') }}</div>
|
||||||
|
<div class="text-[rgb(var(--warning-5))]">{{ scenario.executeFakeErrorCount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<MsButton
|
<MsButton
|
||||||
v-if="scenario.isDebug === false && !scenario.executeLoading && !scenario.isNew"
|
v-if="scenario.isDebug === false && !scenario.executeLoading && !scenario.isNew"
|
||||||
|
|
|
@ -133,7 +133,7 @@
|
||||||
>
|
>
|
||||||
<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-[120px] px-[8px]"
|
||||||
size="mini"
|
size="mini"
|
||||||
:step="1"
|
:step="1"
|
||||||
:min="0"
|
:min="0"
|
||||||
|
|
|
@ -152,7 +152,7 @@
|
||||||
<div
|
<div
|
||||||
:class="`one-line-text mr-[4px] ${
|
:class="`one-line-text mr-[4px] ${
|
||||||
step.stepType === ScenarioStepType.ONCE_ONLY_CONTROLLER ? 'max-w-[750px]' : 'max-w-[150px]'
|
step.stepType === ScenarioStepType.ONCE_ONLY_CONTROLLER ? 'max-w-[750px]' : 'max-w-[150px]'
|
||||||
} font-normal text-[var(--color-text-4)]`"
|
} font-normal text-[var(--color-text-1)]`"
|
||||||
>
|
>
|
||||||
{{ step.name || t('apiScenario.pleaseInputStepDesc') }}
|
{{ step.name || t('apiScenario.pleaseInputStepDesc') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -190,27 +190,11 @@
|
||||||
</template>
|
</template>
|
||||||
<template #extraEnd="step">
|
<template #extraEnd="step">
|
||||||
<responsePopover
|
<responsePopover
|
||||||
v-if="
|
|
||||||
[
|
|
||||||
ScenarioStepType.API,
|
|
||||||
ScenarioStepType.API_CASE,
|
|
||||||
ScenarioStepType.SCRIPT,
|
|
||||||
ScenarioStepType.CUSTOM_REQUEST,
|
|
||||||
].includes(step.stepType) &&
|
|
||||||
(getExecuteStatus(step) === ScenarioExecuteStatus.SUCCESS ||
|
|
||||||
getExecuteStatus(step) === ScenarioExecuteStatus.FAILED)
|
|
||||||
"
|
|
||||||
:step="step"
|
:step="step"
|
||||||
:step-responses="scenario.stepResponses"
|
:step-responses="scenario.stepResponses"
|
||||||
|
:final-execute-status="getExecuteStatus(step)"
|
||||||
@visible-change="handleResponsePopoverVisibleChange"
|
@visible-change="handleResponsePopoverVisibleChange"
|
||||||
/>
|
/>
|
||||||
<executeStatus
|
|
||||||
v-else-if="step.executeStatus"
|
|
||||||
:status="getExecuteStatus(step)"
|
|
||||||
:extra-text="getExecuteStatusExtraText(step)"
|
|
||||||
size="small"
|
|
||||||
class="ml-[4px]"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-if="steps.length === 0 && stepKeyword.trim() !== ''" #empty>
|
<template v-if="steps.length === 0 && stepKeyword.trim() !== ''" #empty>
|
||||||
<div
|
<div
|
||||||
|
@ -240,24 +224,30 @@
|
||||||
:request="currentStepDetail as unknown as RequestParam"
|
:request="currentStepDetail as unknown as RequestParam"
|
||||||
:file-params="currentStepFileParams"
|
:file-params="currentStepFileParams"
|
||||||
:step="activeStep"
|
:step="activeStep"
|
||||||
|
:scenario-id="scenario.id"
|
||||||
:step-responses="scenario.stepResponses"
|
:step-responses="scenario.stepResponses"
|
||||||
:permission-map="permissionMap"
|
:permission-map="permissionMap"
|
||||||
|
:steps="steps"
|
||||||
@add-step="addCustomApiStep"
|
@add-step="addCustomApiStep"
|
||||||
@apply-step="applyApiStep"
|
@apply-step="applyApiStep"
|
||||||
@stop-debug="handleStopExecute(activeStep)"
|
@stop-debug="handleStopExecute(activeStep)"
|
||||||
@execute="handleApiExecute"
|
@execute="handleApiExecute"
|
||||||
|
@replace="handleReplaceStep"
|
||||||
/>
|
/>
|
||||||
<customCaseDrawer
|
<customCaseDrawer
|
||||||
v-model:visible="customCaseDrawerVisible"
|
v-model:visible="customCaseDrawerVisible"
|
||||||
:active-step="activeStep"
|
:active-step="activeStep"
|
||||||
:request="currentStepDetail as unknown as RequestParam"
|
:request="currentStepDetail as unknown as RequestParam"
|
||||||
|
:scenario-id="scenario.id"
|
||||||
:file-params="currentStepFileParams"
|
:file-params="currentStepFileParams"
|
||||||
|
:steps="steps"
|
||||||
:step-responses="scenario.stepResponses"
|
:step-responses="scenario.stepResponses"
|
||||||
:permission-map="permissionMap"
|
:permission-map="permissionMap"
|
||||||
@apply-step="applyApiStep"
|
@apply-step="applyApiStep"
|
||||||
@delete-step="deleteCaseStep(activeStep)"
|
@delete-step="deleteCaseStep(activeStep)"
|
||||||
@stop-debug="handleStopExecute(activeStep)"
|
@stop-debug="handleStopExecute(activeStep)"
|
||||||
@execute="(request, executeType) => handleApiExecute((request as unknown as RequestParam), executeType)"
|
@execute="(request, executeType) => handleApiExecute((request as unknown as RequestParam), executeType)"
|
||||||
|
@replace="handleReplaceStep"
|
||||||
/>
|
/>
|
||||||
<importApiDrawer
|
<importApiDrawer
|
||||||
v-if="importApiDrawerVisible"
|
v-if="importApiDrawerVisible"
|
||||||
|
@ -451,7 +441,6 @@
|
||||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||||
import { MsTreeExpandedData, MsTreeNodeData } from '@/components/business/ms-tree/types';
|
import { MsTreeExpandedData, MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
import executeStatus from '../common/executeStatus.vue';
|
|
||||||
import { ImportData } from '../common/importApiDrawer/index.vue';
|
import { ImportData } from '../common/importApiDrawer/index.vue';
|
||||||
import stepType from '../common/stepType/stepType.vue';
|
import stepType from '../common/stepType/stepType.vue';
|
||||||
import createStepActions from './createAction/createStepActions.vue';
|
import createStepActions from './createAction/createStepActions.vue';
|
||||||
|
@ -496,7 +485,6 @@
|
||||||
RequestDefinitionStatus,
|
RequestDefinitionStatus,
|
||||||
ScenarioAddStepActionType,
|
ScenarioAddStepActionType,
|
||||||
ScenarioExecuteStatus,
|
ScenarioExecuteStatus,
|
||||||
ScenarioStepLoopTypeEnum,
|
|
||||||
ScenarioStepRefType,
|
ScenarioStepRefType,
|
||||||
ScenarioStepType,
|
ScenarioStepType,
|
||||||
} from '@/enums/apiEnum';
|
} from '@/enums/apiEnum';
|
||||||
|
@ -569,32 +557,20 @@
|
||||||
|
|
||||||
function getExecuteStatus(step: ScenarioStepItem) {
|
function getExecuteStatus(step: ScenarioStepItem) {
|
||||||
if (scenario.value.stepResponses && scenario.value.stepResponses[step.uniqueId]) {
|
if (scenario.value.stepResponses && scenario.value.stepResponses[step.uniqueId]) {
|
||||||
|
if (
|
||||||
|
scenario.value.stepResponses[step.uniqueId].some((report) => report.status === ScenarioExecuteStatus.FAKE_ERROR)
|
||||||
|
) {
|
||||||
|
return ScenarioExecuteStatus.FAKE_ERROR;
|
||||||
|
}
|
||||||
// 有一次失败就是失败
|
// 有一次失败就是失败
|
||||||
return scenario.value.stepResponses[step.uniqueId].some((report) => !report.isSuccessful)
|
if (scenario.value.stepResponses[step.uniqueId].some((report) => !report.isSuccessful)) {
|
||||||
? ScenarioExecuteStatus.FAILED
|
return ScenarioExecuteStatus.FAILED;
|
||||||
: ScenarioExecuteStatus.SUCCESS;
|
}
|
||||||
|
return ScenarioExecuteStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
return step.executeStatus;
|
return step.executeStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExecuteStatusExtraText(step: ScenarioStepItem) {
|
|
||||||
if (
|
|
||||||
step.stepType === ScenarioStepType.LOOP_CONTROLLER &&
|
|
||||||
step.config.loopType === ScenarioStepLoopTypeEnum.LOOP_COUNT &&
|
|
||||||
step.config.msCountController &&
|
|
||||||
step.config.msCountController.loops > 0
|
|
||||||
) {
|
|
||||||
// 循环控制器展示当前执行次数/总次数
|
|
||||||
const firstHasResultChild = step.children?.find((child) => {
|
|
||||||
return checkStepIsApi(child) || child.stepType === ScenarioStepType.SCRIPT;
|
|
||||||
});
|
|
||||||
return firstHasResultChild && scenario.value.stepResponses[firstHasResultChild.uniqueId]
|
|
||||||
? `${scenario.value.stepResponses[firstHasResultChild.uniqueId].length}/${step.config.msCountController.loops}`
|
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResponsePopoverVisibleChange(visible: boolean, step: ScenarioStepItem) {
|
function handleResponsePopoverVisibleChange(visible: boolean, step: ScenarioStepItem) {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
setFocusNodeKey(step.uniqueId);
|
setFocusNodeKey(step.uniqueId);
|
||||||
|
@ -914,8 +890,8 @@
|
||||||
...cloneDeep(
|
...cloneDeep(
|
||||||
mapTree<ScenarioStepItem>(node, (childNode) => {
|
mapTree<ScenarioStepItem>(node, (childNode) => {
|
||||||
const childId = getGenerateId();
|
const childId = getGenerateId();
|
||||||
const childStepDetail = stepDetails.value[node.id];
|
const childStepDetail = stepDetails.value[childNode.id];
|
||||||
const childStepFileParam = scenario.value.stepFileParam[node.id];
|
const childStepFileParam = scenario.value.stepFileParam[childNode.id];
|
||||||
let childCopyFromStepId = childNode.id;
|
let childCopyFromStepId = childNode.id;
|
||||||
if (childStepDetail) {
|
if (childStepDetail) {
|
||||||
// 如果复制的步骤下子步骤还有详情数据,则也复制详情数据
|
// 如果复制的步骤下子步骤还有详情数据,则也复制详情数据
|
||||||
|
@ -923,7 +899,7 @@
|
||||||
}
|
}
|
||||||
if (childStepFileParam) {
|
if (childStepFileParam) {
|
||||||
// 如果复制的步骤下子步骤还有详情数据,则也复制详情数据
|
// 如果复制的步骤下子步骤还有详情数据,则也复制详情数据
|
||||||
scenario.value.stepFileParam[id] = cloneDeep(childStepFileParam);
|
scenario.value.stepFileParam[childNode.id] = cloneDeep(childStepFileParam);
|
||||||
}
|
}
|
||||||
if (!isQuoteScenario) {
|
if (!isQuoteScenario) {
|
||||||
// 非引用场景才处理复制来源 id
|
// 非引用场景才处理复制来源 id
|
||||||
|
@ -1211,7 +1187,7 @@
|
||||||
const res = await debugScenario({
|
const res = await debugScenario({
|
||||||
id: scenario.value.id || '',
|
id: scenario.value.id || '',
|
||||||
grouped: false,
|
grouped: false,
|
||||||
environmentId: currentEnvConfig?.value.id || '',
|
environmentId: currentEnvConfig?.value?.id || '',
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
scenarioConfig: scenario.value.scenarioConfig,
|
scenarioConfig: scenario.value.scenarioConfig,
|
||||||
frontendDebug: executeType === 'localExec',
|
frontendDebug: executeType === 'localExec',
|
||||||
|
@ -1302,6 +1278,7 @@
|
||||||
} else {
|
} else {
|
||||||
// 步骤列表找不到该步骤,说明是新建的自定义请求还未保存,则临时创建一个步骤进行调试(不保存步骤信息)
|
// 步骤列表找不到该步骤,说明是新建的自定义请求还未保存,则临时创建一个步骤进行调试(不保存步骤信息)
|
||||||
const reportId = getGenerateId();
|
const reportId = getGenerateId();
|
||||||
|
delete scenario.value.stepResponses[request.stepId]; // 先移除上一次的执行结果
|
||||||
request.executeLoading = true;
|
request.executeLoading = true;
|
||||||
activeStep.value = {
|
activeStep.value = {
|
||||||
id: request.stepId,
|
id: request.stepId,
|
||||||
|
@ -1343,6 +1320,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleReplaceStep(newStep: ScenarioStepItem) {
|
||||||
|
if (activeStep.value) {
|
||||||
|
// 替换步骤,删除原本的详情数据
|
||||||
|
delete scenario.value.stepResponses[activeStep.value.uniqueId];
|
||||||
|
delete scenario.value.stepFileParam[activeStep.value.id];
|
||||||
|
delete stepDetails.value[activeStep.value.id];
|
||||||
|
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, activeStep.value.uniqueId, 'uniqueId');
|
||||||
|
if (realStep) {
|
||||||
|
// 将旧步骤替换为新步骤
|
||||||
|
if (realStep.parent?.children) {
|
||||||
|
// 如果被替换的步骤是子孙步骤,则需要在父级的 children 中替换
|
||||||
|
const index = realStep.parent.children.findIndex((item) => item.uniqueId === realStep.uniqueId);
|
||||||
|
realStep.parent.children.splice(index, 1, newStep);
|
||||||
|
} else {
|
||||||
|
// 如果被替换的步骤是第一层级步骤,则直接替换
|
||||||
|
const index = steps.value.findIndex((item) => item.uniqueId === realStep.uniqueId);
|
||||||
|
steps.value.splice(index, 1, newStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message.success(t('apiScenario.replaceSuccess'));
|
||||||
|
scenario.value.unSaved = true;
|
||||||
|
if (newStep.stepType === ScenarioStepType.API_SCENARIO) {
|
||||||
|
customCaseDrawerVisible.value = false;
|
||||||
|
customApiDrawerVisible.value = false;
|
||||||
|
} else {
|
||||||
|
customCaseDrawerVisible.value = false;
|
||||||
|
customApiDrawerVisible.value = false;
|
||||||
|
nextTick(() => {
|
||||||
|
// 等待抽屉关闭后再打开新的抽屉
|
||||||
|
handleStepSelect([newStep.uniqueId], newStep);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理抽屉资源类型步骤创建动作
|
* 处理抽屉资源类型步骤创建动作
|
||||||
*/
|
*/
|
||||||
|
@ -1609,7 +1621,7 @@
|
||||||
}
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const offspringIds: string[] = [];
|
const offspringIds: string[] = [];
|
||||||
mapTree(dragNode.children || [], (e) => {
|
mapTree(cloneDeep(dragNode.children || []), (e) => {
|
||||||
offspringIds.push(e.uniqueId);
|
offspringIds.push(e.uniqueId);
|
||||||
return e;
|
return e;
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,6 +27,7 @@ export default function updateStepStatus(
|
||||||
// 逻辑控制器和场景内部可以放入任意步骤,所以它的最终执行结果是根据内部步骤的执行结果来判断的
|
// 逻辑控制器和场景内部可以放入任意步骤,所以它的最终执行结果是根据内部步骤的执行结果来判断的
|
||||||
let hasNotExecuted = false;
|
let hasNotExecuted = false;
|
||||||
let hasFailure = false;
|
let hasFailure = false;
|
||||||
|
let hasFakeError = false;
|
||||||
if (!node.children || node.children.length === 0) {
|
if (!node.children || node.children.length === 0) {
|
||||||
// 逻辑控制器内无步骤,则直接是未执行
|
// 逻辑控制器内无步骤,则直接是未执行
|
||||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
|
@ -43,13 +44,18 @@ export default function updateStepStatus(
|
||||||
} else if (childNode.executeStatus === ScenarioExecuteStatus.FAILED) {
|
} else if (childNode.executeStatus === ScenarioExecuteStatus.FAILED) {
|
||||||
// 子节点有一个失败,逻辑控制器就是失败
|
// 子节点有一个失败,逻辑控制器就是失败
|
||||||
hasFailure = true;
|
hasFailure = true;
|
||||||
|
} else if (childNode.executeStatus === ScenarioExecuteStatus.FAKE_ERROR) {
|
||||||
|
// 子节点有一个误报,逻辑控制器就是误报
|
||||||
|
hasFakeError = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 递归完子节点后,判断当前逻辑控制器的状态
|
// 递归完子节点后,判断当前逻辑控制器的状态
|
||||||
if (hasFailure) {
|
if (hasNotExecuted) {
|
||||||
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
|
||||||
} else if (hasNotExecuted) {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
|
} else if (hasFailure) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
||||||
|
} else if (hasFakeError) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.FAKE_ERROR;
|
||||||
} else {
|
} else {
|
||||||
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -60,9 +66,13 @@ export default function updateStepStatus(
|
||||||
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
||||||
// 非逻辑控制器直接更改本身状态
|
// 非逻辑控制器直接更改本身状态
|
||||||
if (stepResponses[node.uniqueId] && stepResponses[node.uniqueId].length > 0) {
|
if (stepResponses[node.uniqueId] && stepResponses[node.uniqueId].length > 0) {
|
||||||
node.executeStatus = stepResponses[node.uniqueId].some((report) => !report.isSuccessful)
|
if (stepResponses[node.uniqueId].some((report) => !report.isSuccessful)) {
|
||||||
? ScenarioExecuteStatus.FAILED
|
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
||||||
: ScenarioExecuteStatus.SUCCESS;
|
} else if (stepResponses[node.uniqueId].some((report) => report.status === ScenarioExecuteStatus.FAKE_ERROR)) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.FAKE_ERROR;
|
||||||
|
} else {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
</template>
|
</template>
|
||||||
</MsEditableTab>
|
</MsEditableTab>
|
||||||
<div v-show="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]">
|
<div v-show="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]">
|
||||||
<environmentSelect v-model:current-env-config="currentEnvConfig" />
|
<environmentSelect
|
||||||
|
v-model:currentEnv="activeScenarioTab.environmentId"
|
||||||
|
v-model:current-env-config="currentEnvConfig"
|
||||||
|
/>
|
||||||
<executeButton
|
<executeButton
|
||||||
ref="executeButtonRef"
|
ref="executeButtonRef"
|
||||||
v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']"
|
v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']"
|
||||||
|
@ -161,6 +164,7 @@
|
||||||
id: 'all',
|
id: 'all',
|
||||||
label: t('apiScenario.allScenario'),
|
label: t('apiScenario.allScenario'),
|
||||||
closable: false,
|
closable: false,
|
||||||
|
environmentId: '',
|
||||||
} as ScenarioParams,
|
} as ScenarioParams,
|
||||||
]);
|
]);
|
||||||
const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams);
|
const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams);
|
||||||
|
@ -195,7 +199,9 @@
|
||||||
...result,
|
...result,
|
||||||
console: data.taskResult.console,
|
console: data.taskResult.console,
|
||||||
});
|
});
|
||||||
if (result.isSuccessful) {
|
if (result.status === ScenarioExecuteStatus.FAKE_ERROR) {
|
||||||
|
scenario.executeFakeErrorCount += 1;
|
||||||
|
} else if (result.isSuccessful) {
|
||||||
scenario.executeSuccessCount += 1;
|
scenario.executeSuccessCount += 1;
|
||||||
} else {
|
} else {
|
||||||
scenario.executeFailCount += 1;
|
scenario.executeFailCount += 1;
|
||||||
|
@ -233,6 +239,7 @@
|
||||||
activeScenarioTab.value.executeTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
activeScenarioTab.value.executeTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
activeScenarioTab.value.executeSuccessCount = 0;
|
activeScenarioTab.value.executeSuccessCount = 0;
|
||||||
activeScenarioTab.value.executeFailCount = 0;
|
activeScenarioTab.value.executeFailCount = 0;
|
||||||
|
activeScenarioTab.value.executeFakeErrorCount = 0;
|
||||||
activeScenarioTab.value.stepResponses = {};
|
activeScenarioTab.value.stepResponses = {};
|
||||||
activeScenarioTab.value.reportId = executeParams.reportId; // 存储报告ID
|
activeScenarioTab.value.reportId = executeParams.reportId; // 存储报告ID
|
||||||
activeScenarioTab.value.isDebug = !isExecute;
|
activeScenarioTab.value.isDebug = !isExecute;
|
||||||
|
@ -403,6 +410,7 @@
|
||||||
scenarioTabs.value.push({
|
scenarioTabs.value.push({
|
||||||
...cloneDeep(defaultScenario),
|
...cloneDeep(defaultScenario),
|
||||||
id: getGenerateId(),
|
id: getGenerateId(),
|
||||||
|
environmentId: currentEnvConfig.value?.id || '',
|
||||||
label: `${t('apiScenario.createScenario')}${scenarioTabs.value.length}`,
|
label: `${t('apiScenario.createScenario')}${scenarioTabs.value.length}`,
|
||||||
moduleId: activeModule.value === 'all' ? 'root' : activeModule.value,
|
moduleId: activeModule.value === 'all' ? 'root' : activeModule.value,
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
|
|
|
@ -70,6 +70,7 @@ export default {
|
||||||
'apiScenario.executionResult': '执行结果',
|
'apiScenario.executionResult': '执行结果',
|
||||||
'apiScenario.refreshRefScenario': '刷新引用场景数据',
|
'apiScenario.refreshRefScenario': '刷新引用场景数据',
|
||||||
'apiScenario.updateRefScenarioSuccess': '引用场景数据已更新',
|
'apiScenario.updateRefScenarioSuccess': '引用场景数据已更新',
|
||||||
|
'apiScenario.replaceSuccess': '步骤替换成功',
|
||||||
// 批量操作文案
|
// 批量操作文案
|
||||||
'api_scenario.batch_operation.success': '成功{opt}至 {name}',
|
'api_scenario.batch_operation.success': '成功{opt}至 {name}',
|
||||||
'api_scenario.table.batchMoveConfirm': '{opt}{count}个场景至已选模块',
|
'api_scenario.table.batchMoveConfirm': '{opt}{count}个场景至已选模块',
|
||||||
|
|
Loading…
Reference in New Issue