refactor(接口场景): 场景执行状态优化&部分表格筛选状态初始化为空
This commit is contained in:
parent
7fe24a0d27
commit
9f67d9fb6b
|
@ -185,8 +185,8 @@ export function calculateMaxDepth(arr?: Node[], depth = 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeNode<T> {
|
export interface TreeNode<T> {
|
||||||
[key: string]: any;
|
|
||||||
children?: TreeNode<T>[];
|
children?: TreeNode<T>[];
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -504,9 +504,9 @@
|
||||||
];
|
];
|
||||||
|
|
||||||
const methodFilterVisible = ref(false);
|
const methodFilterVisible = ref(false);
|
||||||
const methodFilters = ref(Object.keys(RequestMethods));
|
const methodFilters = ref<string[]>([]);
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
const statusFilters = ref<string[]>([]);
|
||||||
|
|
||||||
const tableStore = useTableStore();
|
const tableStore = useTableStore();
|
||||||
async function getModuleIds() {
|
async function getModuleIds() {
|
||||||
|
@ -529,9 +529,8 @@
|
||||||
moduleIds,
|
moduleIds,
|
||||||
protocol: props.protocol,
|
protocol: props.protocol,
|
||||||
filter: {
|
filter: {
|
||||||
status:
|
status: statusFilters.value,
|
||||||
statusFilters.value.length === Object.keys(RequestDefinitionStatus).length ? undefined : statusFilters.value,
|
method: methodFilters.value,
|
||||||
method: methodFilters.value.length === Object.keys(RequestMethods).length ? undefined : methodFilters.value,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
setLoadListParams(params);
|
setLoadListParams(params);
|
||||||
|
|
|
@ -560,7 +560,7 @@
|
||||||
];
|
];
|
||||||
|
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
const statusFilters = ref<string[]>([]);
|
||||||
const caseLevelFields = ref<Record<string, any>>({});
|
const caseLevelFields = ref<Record<string, any>>({});
|
||||||
const caseFilterVisible = ref(false);
|
const caseFilterVisible = ref(false);
|
||||||
const caseFilters = ref<string[]>([]);
|
const caseFilters = ref<string[]>([]);
|
||||||
|
@ -571,7 +571,7 @@
|
||||||
const lastReportStatusList = computed(() => {
|
const lastReportStatusList = computed(() => {
|
||||||
return Object.keys(ReportStatus[ReportEnum.API_REPORT]);
|
return Object.keys(ReportStatus[ReportEnum.API_REPORT]);
|
||||||
});
|
});
|
||||||
const lastReportStatusFilters = ref<string[]>(Object.keys(ReportStatus[ReportEnum.API_REPORT]));
|
const lastReportStatusFilters = ref<string[]>([]);
|
||||||
|
|
||||||
async function getModuleIds() {
|
async function getModuleIds() {
|
||||||
let moduleIds: string[] = [];
|
let moduleIds: string[] = [];
|
||||||
|
|
|
@ -118,10 +118,10 @@
|
||||||
import { ApiCaseExecuteHistoryItem } from '@/models/apiTest/management';
|
import { ApiCaseExecuteHistoryItem } from '@/models/apiTest/management';
|
||||||
import { ReportEnum, ReportStatus, TriggerModeLabel } from '@/enums/reportEnum';
|
import { ReportEnum, ReportStatus, TriggerModeLabel } from '@/enums/reportEnum';
|
||||||
|
|
||||||
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
|
const triggerModeListFilters = ref<string[]>();
|
||||||
const triggerModeFilterVisible = ref(false);
|
const triggerModeFilterVisible = ref(false);
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref<string[]>(Object.keys(ReportStatus[ReportEnum.API_REPORT]));
|
const statusFilters = ref<string[]>();
|
||||||
const statusList = computed(() => {
|
const statusList = computed(() => {
|
||||||
return Object.keys(ReportStatus[ReportEnum.API_REPORT]);
|
return Object.keys(ReportStatus[ReportEnum.API_REPORT]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -256,9 +256,9 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const methodFilterVisible = ref(false);
|
const methodFilterVisible = ref(false);
|
||||||
const methodFilters = ref(Object.keys(RequestMethods));
|
const methodFilters = ref<string[]>([]);
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
const statusFilters = ref<string[]>([]);
|
||||||
const moduleIds = computed(() => {
|
const moduleIds = computed(() => {
|
||||||
if (props.activeModule === 'all') {
|
if (props.activeModule === 'all') {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -366,7 +366,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
const statusFilters = ref<string[]>([]);
|
||||||
const caseLevelFields = ref<Record<string, any>>({});
|
const caseLevelFields = ref<Record<string, any>>({});
|
||||||
const caseFilterVisible = ref(false);
|
const caseFilterVisible = ref(false);
|
||||||
const caseFilters = ref<string[]>([]);
|
const caseFilters = ref<string[]>([]);
|
||||||
|
@ -375,7 +375,7 @@
|
||||||
});
|
});
|
||||||
const lastReportStatusFilterVisible = ref(false);
|
const lastReportStatusFilterVisible = ref(false);
|
||||||
const lastReportStatusList = ['error', 'FakeError', 'success'];
|
const lastReportStatusList = ['error', 'FakeError', 'success'];
|
||||||
const lastReportStatusFilters = ref<string[]>([...lastReportStatusList]);
|
const lastReportStatusFilters = ref<string[]>([]);
|
||||||
|
|
||||||
const moduleIds = computed(() => {
|
const moduleIds = computed(() => {
|
||||||
return props.activeModule === 'all' ? [] : [props.activeModule];
|
return props.activeModule === 'all' ? [] : [props.activeModule];
|
||||||
|
|
|
@ -174,8 +174,8 @@
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const triggerModeFilterVisible = ref(false);
|
const triggerModeFilterVisible = ref(false);
|
||||||
|
|
||||||
const statusListFilters = ref<string[]>(Object.keys(ReportStatus[props.moduleType]));
|
const statusListFilters = ref<string[]>([]);
|
||||||
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
|
const triggerModeListFilters = ref<string[]>([]);
|
||||||
|
|
||||||
type ReportShowType = 'All' | 'INDEPENDENT' | 'INTEGRATED';
|
type ReportShowType = 'All' | 'INDEPENDENT' | 'INTEGRATED';
|
||||||
const showType = ref<ReportShowType>('All');
|
const showType = ref<ReportShowType>('All');
|
||||||
|
@ -287,7 +287,7 @@
|
||||||
moduleType: props.moduleType,
|
moduleType: props.moduleType,
|
||||||
filter: {
|
filter: {
|
||||||
status: statusListFilters.value,
|
status: statusListFilters.value,
|
||||||
integrated: showType.value === 'All' ? undefined : Array.of((showType.value === 'INTEGRATED').toString()),
|
integrated: Array.of((showType.value === 'INTEGRATED').toString()),
|
||||||
triggerMode: triggerModeListFilters.value,
|
triggerMode: triggerModeListFilters.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="flex items-center gap-[8px]">
|
<div class="flex max-w-[60%] items-center gap-[8px]">
|
||||||
<stepTypeVue
|
<stepTypeVue v-if="props.step" :step="props.step" />
|
||||||
v-if="props.step"
|
<a-tooltip :content="title" position="bottom">
|
||||||
v-show="props.step.stepType !== ScenarioStepType.CUSTOM_REQUEST"
|
<div class="one-line-text">
|
||||||
:step="props.step"
|
{{ title }}
|
||||||
/>
|
</div>
|
||||||
{{ title }}
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
||||||
|
@ -475,7 +475,7 @@
|
||||||
if (_stepType.value.isCopyApi || _stepType.value.isQuoteApi) {
|
if (_stepType.value.isCopyApi || _stepType.value.isQuoteApi) {
|
||||||
return props.step?.name;
|
return props.step?.name;
|
||||||
}
|
}
|
||||||
return t('apiScenario.customApi');
|
return props.step?.name || t('apiScenario.customApi');
|
||||||
});
|
});
|
||||||
const showEnvPrefix = computed(
|
const showEnvPrefix = computed(
|
||||||
() =>
|
() =>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<div v-show="!isShowEditStepNameInput" class="flex flex-1 items-center justify-between">
|
<div v-show="!isShowEditStepNameInput" class="flex flex-1 items-center justify-between">
|
||||||
<div class="flex items-center gap-[8px]">
|
<div class="flex items-center gap-[8px]">
|
||||||
<a-tooltip :content="activeStep?.name">
|
<a-tooltip :content="activeStep?.name">
|
||||||
<span> {{ characterLimit(activeStep?.name) }}</span>
|
<div class="one-line-text max-w-[300px]"> {{ characterLimit(activeStep?.name) }}</div>
|
||||||
</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>
|
||||||
|
|
|
@ -38,8 +38,8 @@
|
||||||
text: 'common.success',
|
text: 'common.success',
|
||||||
},
|
},
|
||||||
[ScenarioExecuteStatus.UN_EXECUTE]: {
|
[ScenarioExecuteStatus.UN_EXECUTE]: {
|
||||||
bgColor: 'var(--color-text-4)',
|
bgColor: 'var(--color-text-n8)',
|
||||||
color: 'var(--color-text-n9)',
|
color: 'var(--color-text-1)',
|
||||||
text: 'apiScenario.unExecute',
|
text: 'apiScenario.unExecute',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -259,9 +259,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const methodFilterVisible = ref(false);
|
const methodFilterVisible = ref(false);
|
||||||
const methodFilters = ref(Object.keys(RequestMethods));
|
const methodFilters = ref<string[]>([]);
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref(Object.keys(RequestDefinitionStatus));
|
const statusFilters = ref<string[]>([]);
|
||||||
const tableSelectedData = ref<MsTableDataItem<ApiCaseDetail | ApiDefinitionDetail | ApiScenarioTableItem>[]>([]);
|
const tableSelectedData = ref<MsTableDataItem<ApiCaseDetail | ApiDefinitionDetail | ApiScenarioTableItem>[]>([]);
|
||||||
const tableSelectedKeys = computed(() => {
|
const tableSelectedKeys = computed(() => {
|
||||||
return tableSelectedData.value.map((e) => e.id);
|
return tableSelectedData.value.map((e) => e.id);
|
||||||
|
@ -332,11 +332,8 @@
|
||||||
moduleIds: ids || props.moduleIds,
|
moduleIds: ids || props.moduleIds,
|
||||||
protocol: props.protocol,
|
protocol: props.protocol,
|
||||||
filter: {
|
filter: {
|
||||||
status:
|
status: statusFilters.value,
|
||||||
statusFilters.value.length === Object.keys(RequestDefinitionStatus).length
|
method: methodFilters.value,
|
||||||
? undefined
|
|
||||||
: statusFilters.value,
|
|
||||||
method: methodFilters.value.length === Object.keys(RequestMethods).length ? undefined : methodFilters.value,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
currentTable.value.loadList();
|
currentTable.value.loadList();
|
||||||
|
@ -377,8 +374,8 @@
|
||||||
function resetTable() {
|
function resetTable() {
|
||||||
currentTable.value.resetSelector();
|
currentTable.value.resetSelector();
|
||||||
keyword.value = '';
|
keyword.value = '';
|
||||||
methodFilters.value = Object.keys(RequestMethods);
|
methodFilters.value = [];
|
||||||
statusFilters.value = Object.keys(RequestDefinitionStatus);
|
statusFilters.value = [];
|
||||||
loadPage();
|
loadPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,10 +101,10 @@
|
||||||
import { ExecuteStatusFilters } from '@/enums/apiEnum';
|
import { ExecuteStatusFilters } from '@/enums/apiEnum';
|
||||||
import { TriggerModeLabel } from '@/enums/reportEnum';
|
import { TriggerModeLabel } from '@/enums/reportEnum';
|
||||||
|
|
||||||
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
|
const triggerModeListFilters = ref<string[]>([]);
|
||||||
const triggerModeFilterVisible = ref(false);
|
const triggerModeFilterVisible = ref(false);
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref(Object.keys(ExecuteStatusFilters));
|
const statusFilters = ref<string[]>([]);
|
||||||
const tableQueryParams = ref<any>();
|
const tableQueryParams = ref<any>();
|
||||||
|
|
||||||
const keyword = ref('');
|
const keyword = ref('');
|
||||||
|
|
|
@ -368,7 +368,7 @@
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const lastReportStatusFilterVisible = ref(false);
|
const lastReportStatusFilterVisible = ref(false);
|
||||||
const lastReportStatusListFilters = ref<string[]>(Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]));
|
const lastReportStatusListFilters = ref<string[]>([]);
|
||||||
const lastReportStatusFilters = computed(() => {
|
const lastReportStatusFilters = computed(() => {
|
||||||
return Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]);
|
return Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]);
|
||||||
});
|
});
|
||||||
|
@ -586,7 +586,7 @@
|
||||||
];
|
];
|
||||||
|
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref(Object.keys(ApiScenarioStatus));
|
const statusFilters = ref<string[]>([]);
|
||||||
const tableStore = useTableStore();
|
const tableStore = useTableStore();
|
||||||
|
|
||||||
const activeModules = computed(() => {
|
const activeModules = computed(() => {
|
||||||
|
@ -621,7 +621,7 @@
|
||||||
moduleIds,
|
moduleIds,
|
||||||
filter: {
|
filter: {
|
||||||
lastReportStatus: lastReportStatusListFilters.value,
|
lastReportStatus: lastReportStatusListFilters.value,
|
||||||
status: statusFilters.value.length === Object.keys(ApiScenarioStatus).length ? undefined : statusFilters.value,
|
status: statusFilters.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
setLoadListParams(params);
|
setLoadListParams(params);
|
||||||
|
|
|
@ -168,18 +168,30 @@
|
||||||
stepTreeRef.value?.checkAll(checkedAll.value);
|
stepTreeRef.value?.checkAll(checkedAll.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(checkedKeys, (val) => {
|
watch(
|
||||||
if (val.length === 0) {
|
() => checkedKeys.value,
|
||||||
checkedAll.value = false;
|
(val) => {
|
||||||
indeterminate.value = false;
|
if (val.length === 0) {
|
||||||
} else if (val.length === totalStepCount.value) {
|
checkedAll.value = false;
|
||||||
checkedAll.value = true;
|
indeterminate.value = false;
|
||||||
indeterminate.value = false;
|
} else if (val.length === totalStepCount.value) {
|
||||||
} else {
|
checkedAll.value = true;
|
||||||
checkedAll.value = false;
|
indeterminate.value = false;
|
||||||
indeterminate.value = true;
|
} else {
|
||||||
|
checkedAll.value = false;
|
||||||
|
indeterminate.value = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => scenario.value.steps.length,
|
||||||
|
() => {
|
||||||
|
checkedKeys.value = [];
|
||||||
|
checkedAll.value = false;
|
||||||
|
indeterminate.value = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
function expandAllStep() {
|
function expandAllStep() {
|
||||||
isExpandAll.value = !isExpandAll.value;
|
isExpandAll.value = !isExpandAll.value;
|
||||||
|
@ -259,14 +271,19 @@
|
||||||
if (!node.enable) {
|
if (!node.enable) {
|
||||||
// 如果步骤未开启,则删除已选 id,方便下面waitingDebugStepDetails详情判断是否携带
|
// 如果步骤未开启,则删除已选 id,方便下面waitingDebugStepDetails详情判断是否携带
|
||||||
checkedKeysSet.delete(node.id);
|
checkedKeysSet.delete(node.id);
|
||||||
|
node.executeStatus = undefined;
|
||||||
} else if (
|
} else if (
|
||||||
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(node.stepType)
|
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(node.stepType)
|
||||||
) {
|
) {
|
||||||
// 请求和场景类型才直接显示执行中,其他控制器需要等待执行完毕才结算执行结果
|
// 请求和场景类型才直接显示执行中,其他控制器需要等待执行完毕才结算执行结果
|
||||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||||
|
} else {
|
||||||
|
// 其他类型步骤不显示执行状态
|
||||||
|
node.executeStatus = undefined;
|
||||||
}
|
}
|
||||||
return !!node.enable;
|
return !!node.enable;
|
||||||
}
|
}
|
||||||
|
node.executeStatus = undefined; // 未选中的步骤不显示执行状态
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
const waitingDebugStepDetails = {};
|
const waitingDebugStepDetails = {};
|
||||||
|
|
|
@ -179,8 +179,14 @@
|
||||||
<template #extraEnd="step">
|
<template #extraEnd="step">
|
||||||
<a-popover
|
<a-popover
|
||||||
v-if="
|
v-if="
|
||||||
getExecuteStatus(step) === ScenarioExecuteStatus.SUCCESS ||
|
![
|
||||||
getExecuteStatus(step) === ScenarioExecuteStatus.FAILED
|
ScenarioStepType.LOOP_CONTROLLER,
|
||||||
|
ScenarioStepType.IF_CONTROLLER,
|
||||||
|
ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
||||||
|
ScenarioStepType.CONSTANT_TIMER,
|
||||||
|
].includes(step.stepType) &&
|
||||||
|
(getExecuteStatus(step) === ScenarioExecuteStatus.SUCCESS ||
|
||||||
|
getExecuteStatus(step) === ScenarioExecuteStatus.FAILED)
|
||||||
"
|
"
|
||||||
position="br"
|
position="br"
|
||||||
content-class="scenario-step-response-popover"
|
content-class="scenario-step-response-popover"
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { RequestResult } from '@/models/apiTest/common';
|
||||||
|
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
|
import { ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调试或执行结束后,调用本方法更新步骤的执行状态
|
||||||
|
* @param steps 响应式的步骤列表
|
||||||
|
*/
|
||||||
|
export default function updateStepStatus(
|
||||||
|
steps: ScenarioStepItem[],
|
||||||
|
stepResponses: Record<string | number, RequestResult>
|
||||||
|
) {
|
||||||
|
for (let i = 0; i < steps.length; i++) {
|
||||||
|
const node = steps[i];
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
ScenarioStepType.LOOP_CONTROLLER,
|
||||||
|
ScenarioStepType.IF_CONTROLLER,
|
||||||
|
ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
||||||
|
].includes(node.stepType)
|
||||||
|
) {
|
||||||
|
// 逻辑控制器内部可以放入任意步骤,所以它的最终执行结果是根据内部步骤的执行结果来判断的
|
||||||
|
let hasNotExecuted = false;
|
||||||
|
let hasFailure = false;
|
||||||
|
if (!node.children || node.children.length === 0) {
|
||||||
|
// 逻辑控制器内无步骤,则直接是未执行
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
|
} else {
|
||||||
|
for (let j = 0; j < node.children.length; j++) {
|
||||||
|
const childNode = node.children[j];
|
||||||
|
updateStepStatus([childNode], stepResponses);
|
||||||
|
if (
|
||||||
|
childNode.executeStatus &&
|
||||||
|
[ScenarioExecuteStatus.EXECUTING, ScenarioExecuteStatus.UN_EXECUTE].includes(childNode.executeStatus)
|
||||||
|
) {
|
||||||
|
// 子节点未执行或正在执行,则逻辑控制器也是未执行
|
||||||
|
hasNotExecuted = true;
|
||||||
|
} else if (childNode.executeStatus === ScenarioExecuteStatus.FAILED) {
|
||||||
|
// 子节点有一个失败,逻辑控制器就是失败
|
||||||
|
hasFailure = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 递归完子节点后,判断当前逻辑控制器的状态
|
||||||
|
if (hasFailure) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.FAILED;
|
||||||
|
} else if (hasNotExecuted) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
|
} else {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (node.stepType === ScenarioStepType.CONSTANT_TIMER) {
|
||||||
|
// 等待时间直接设置为成功
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
|
||||||
|
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
||||||
|
// 非逻辑控制器直接更改本身状态
|
||||||
|
if (stepResponses[node.id]) {
|
||||||
|
node.executeStatus = stepResponses[node.id].isSuccessful
|
||||||
|
? ScenarioExecuteStatus.SUCCESS
|
||||||
|
: ScenarioExecuteStatus.FAILED;
|
||||||
|
} else {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</MsEditableTab>
|
</MsEditableTab>
|
||||||
<div v-if="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:current-env-config="currentEnvConfig" />
|
||||||
<a-button type="primary" :loading="saveLoading" @click="saveScenario">
|
<a-button type="primary" :loading="saveLoading" @click="saveScenario">
|
||||||
{{ t('common.save') }}
|
{{ t('common.save') }}
|
||||||
|
@ -124,10 +124,11 @@
|
||||||
} from '@/models/apiTest/scenario';
|
} from '@/models/apiTest/scenario';
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||||
import { ScenarioExecuteStatus, ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
|
import { ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
||||||
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
import { defaultScenario } from './components/config';
|
import { defaultScenario } from './components/config';
|
||||||
|
import updateStepStatus from './components/utils';
|
||||||
|
|
||||||
// 异步导入
|
// 异步导入
|
||||||
const detail = defineAsyncComponent(() => import('./detail/index.vue'));
|
const detail = defineAsyncComponent(() => import('./detail/index.vue'));
|
||||||
|
@ -147,11 +148,215 @@
|
||||||
} as ScenarioParams,
|
} as ScenarioParams,
|
||||||
]);
|
]);
|
||||||
const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams);
|
const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams);
|
||||||
|
const currentEnvConfig = ref<EnvConfig>();
|
||||||
|
const executeButtonRef = ref<InstanceType<typeof executeButton>>();
|
||||||
|
|
||||||
|
const websocket = ref<WebSocket>();
|
||||||
|
const temporaryScenarioReportMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||||
|
|
||||||
|
function setStepExecuteStatus() {
|
||||||
|
updateStepStatus(activeScenarioTab.value.steps, activeScenarioTab.value.stepResponses);
|
||||||
|
// activeScenarioTab.value.steps = mapTree<ScenarioStepItem>(activeScenarioTab.value.steps, (step) => {
|
||||||
|
// if (step.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
||||||
|
// // 如果结束执行时还有步骤是执行中状态,则设置为未执行
|
||||||
|
// step.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
|
||||||
|
// }
|
||||||
|
// return step;
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开启websocket监听,接收执行结果
|
||||||
|
*/
|
||||||
|
function debugSocket(reportId?: string | number, executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
|
||||||
|
websocket.value = getSocket(
|
||||||
|
reportId || '',
|
||||||
|
executeType === 'localExec' ? '/ws/debug' : '',
|
||||||
|
executeType === 'localExec' ? localExecuteUrl : ''
|
||||||
|
);
|
||||||
|
websocket.value.addEventListener('message', (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
if (data.msgType === 'EXEC_RESULT') {
|
||||||
|
if (activeScenarioTab.value.reportId === data.reportId) {
|
||||||
|
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||||
|
data.taskResult.requestResults.forEach((result) => {
|
||||||
|
activeScenarioTab.value.stepResponses[result.stepId] = {
|
||||||
|
...result,
|
||||||
|
console: data.taskResult.console,
|
||||||
|
};
|
||||||
|
if (result.isSuccessful) {
|
||||||
|
activeScenarioTab.value.executeSuccessCount += 1;
|
||||||
|
} else {
|
||||||
|
activeScenarioTab.value.executeFailCount += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
||||||
|
data.taskResult.requestResults.forEach((result) => {
|
||||||
|
if (activeScenarioTab.value.reportId) {
|
||||||
|
if (temporaryScenarioReportMap[activeScenarioTab.value.reportId] === undefined) {
|
||||||
|
temporaryScenarioReportMap[activeScenarioTab.value.reportId] = {};
|
||||||
|
}
|
||||||
|
temporaryScenarioReportMap[activeScenarioTab.value.reportId][result.stepId] = {
|
||||||
|
...result,
|
||||||
|
console: data.taskResult.console,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (data.msgType === 'EXEC_END') {
|
||||||
|
// 执行结束,关闭websocket
|
||||||
|
websocket.value?.close();
|
||||||
|
if (activeScenarioTab.value.reportId === data.reportId) {
|
||||||
|
activeScenarioTab.value.executeLoading = false;
|
||||||
|
activeScenarioTab.value.isExecute = false;
|
||||||
|
setStepExecuteStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际执行函数
|
||||||
|
* @param executeParams 执行参数
|
||||||
|
* @param isExecute 是否执行,否则是调试
|
||||||
|
* @param executeType 执行类型
|
||||||
|
* @param localExecuteUrl 本地执行地址
|
||||||
|
*/
|
||||||
|
async function realExecute(
|
||||||
|
executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId'>,
|
||||||
|
isExecute?: boolean,
|
||||||
|
executeType?: 'localExec' | 'serverExec',
|
||||||
|
localExecuteUrl?: string
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
activeScenarioTab.value.executeLoading = true;
|
||||||
|
debugSocket(executeParams.reportId, executeType, localExecuteUrl); // 开启websocket
|
||||||
|
// 重置执行结果
|
||||||
|
activeScenarioTab.value.executeTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
activeScenarioTab.value.executeSuccessCount = 0;
|
||||||
|
activeScenarioTab.value.executeFailCount = 0;
|
||||||
|
activeScenarioTab.value.stepResponses = {};
|
||||||
|
activeScenarioTab.value.reportId = executeParams.reportId; // 存储报告ID
|
||||||
|
activeScenarioTab.value.isDebug = !isExecute;
|
||||||
|
let res;
|
||||||
|
if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) {
|
||||||
|
// 执行场景且非本地执行且非未保存场景
|
||||||
|
res = await executeScenario({
|
||||||
|
id: activeScenarioTab.value.id,
|
||||||
|
grouped: false,
|
||||||
|
environmentId: currentEnvConfig.value?.id || '',
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
||||||
|
uploadFileIds: activeScenarioTab.value.uploadFileIds,
|
||||||
|
linkFileIds: activeScenarioTab.value.linkFileIds,
|
||||||
|
...executeParams,
|
||||||
|
steps: mapTree(executeParams.steps, (node) => {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res = await debugScenario({
|
||||||
|
id: activeScenarioTab.value.id,
|
||||||
|
grouped: false,
|
||||||
|
environmentId: currentEnvConfig.value?.id || '',
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
||||||
|
uploadFileIds: activeScenarioTab.value.uploadFileIds,
|
||||||
|
linkFileIds: activeScenarioTab.value.linkFileIds,
|
||||||
|
frontendDebug: executeType === 'localExec',
|
||||||
|
...executeParams,
|
||||||
|
steps: mapTree(executeParams.steps, (node) => {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (executeType === 'localExec' && localExecuteUrl) {
|
||||||
|
// 本地执行需要调 debug 接口获取响应结果,然后再调本地执行接口
|
||||||
|
await localExecuteApiDebug(localExecuteUrl, res);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
websocket.value?.close();
|
||||||
|
activeScenarioTab.value.executeLoading = false;
|
||||||
|
setStepExecuteStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行场景
|
||||||
|
* @param executeType 执行类型
|
||||||
|
* @param localExecuteUrl 本地执行地址
|
||||||
|
*/
|
||||||
|
function handleExecute(executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
|
||||||
|
const waitingDebugStepDetails = {};
|
||||||
|
const waitTingDebugSteps = filterTree(activeScenarioTab.value.steps, (node) => {
|
||||||
|
if (node.enable) {
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||||
|
waitingDebugStepDetails[node.id] = activeScenarioTab.value.stepDetails[node.id];
|
||||||
|
if (
|
||||||
|
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(node.stepType)
|
||||||
|
) {
|
||||||
|
// 请求和场景类型才直接显示执行中,其他控制器需要等待执行完毕才结算执行结果
|
||||||
|
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !!node.enable;
|
||||||
|
});
|
||||||
|
realExecute(
|
||||||
|
{
|
||||||
|
steps: waitTingDebugSteps,
|
||||||
|
stepDetails: waitingDebugStepDetails,
|
||||||
|
reportId: getGenerateId(),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
executeType,
|
||||||
|
localExecuteUrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStopExecute() {
|
||||||
|
websocket.value?.close();
|
||||||
|
activeScenarioTab.value.executeLoading = false;
|
||||||
|
setStepExecuteStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => activeScenarioTab.value.id,
|
||||||
|
(val) => {
|
||||||
|
if (val !== 'all' && activeScenarioTab.value.reportId && !activeScenarioTab.value.executeLoading) {
|
||||||
|
// 当前查看的 tab 非全部场景 tab 页,且当前场景有报告ID,且不是正在执行中,则读取缓存报告
|
||||||
|
const cacheReport = temporaryScenarioReportMap[activeScenarioTab.value.reportId];
|
||||||
|
if (cacheReport) {
|
||||||
|
// 如果有缓存的报告未读取,则直接赋值
|
||||||
|
Object.keys(cacheReport).forEach((stepId) => {
|
||||||
|
const result = cacheReport[stepId];
|
||||||
|
activeScenarioTab.value.stepResponses[stepId] = result;
|
||||||
|
if (result.isSuccessful) {
|
||||||
|
activeScenarioTab.value.executeSuccessCount += 1;
|
||||||
|
} else {
|
||||||
|
activeScenarioTab.value.executeFailCount += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
activeScenarioTab.value.executeLoading = false;
|
||||||
|
delete temporaryScenarioReportMap[activeScenarioTab.value.reportId]; // 取完释放缓存
|
||||||
|
setStepExecuteStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
function newTab(defaultScenarioInfo?: Scenario, action?: 'copy' | 'execute') {
|
function newTab(defaultScenarioInfo?: Scenario, action?: 'copy' | 'execute') {
|
||||||
if (defaultScenarioInfo) {
|
if (defaultScenarioInfo) {
|
||||||
const isCopy = action === 'copy';
|
const isCopy = action === 'copy';
|
||||||
let copySteps = defaultScenarioInfo.steps;
|
let copySteps: ScenarioStepItem[] = [];
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
copySteps = mapTree(defaultScenarioInfo.steps, (node) => {
|
copySteps = mapTree(defaultScenarioInfo.steps, (node) => {
|
||||||
return {
|
return {
|
||||||
|
@ -160,6 +365,8 @@
|
||||||
id: getGenerateId(),
|
id: getGenerateId(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
copySteps = mapTree(defaultScenarioInfo.steps);
|
||||||
}
|
}
|
||||||
scenarioTabs.value.push({
|
scenarioTabs.value.push({
|
||||||
...defaultScenarioInfo,
|
...defaultScenarioInfo,
|
||||||
|
@ -168,9 +375,14 @@
|
||||||
label: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
label: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
||||||
name: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
name: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
||||||
isNew: isCopy,
|
isNew: isCopy,
|
||||||
isExecute: action === 'execute',
|
|
||||||
stepResponses: {},
|
stepResponses: {},
|
||||||
});
|
});
|
||||||
|
if (action === 'execute') {
|
||||||
|
nextTick(() => {
|
||||||
|
// 等待激活 tab 设置完毕后执行
|
||||||
|
handleExecute(executeButtonRef.value?.isPriorityLocalExec ? 'localExec' : 'serverExec');
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
scenarioTabs.value.push({
|
scenarioTabs.value.push({
|
||||||
...cloneDeep(defaultScenario),
|
...cloneDeep(defaultScenario),
|
||||||
|
@ -190,8 +402,6 @@
|
||||||
const activeFolder = ref<string>('all');
|
const activeFolder = ref<string>('all');
|
||||||
const offspringIds = ref<string[]>([]);
|
const offspringIds = ref<string[]>([]);
|
||||||
const isShowScenario = ref(false);
|
const isShowScenario = ref(false);
|
||||||
const executeButtonRef = ref<InstanceType<typeof executeButton>>();
|
|
||||||
const currentEnvConfig = ref<EnvConfig>();
|
|
||||||
|
|
||||||
// 获取激活用例类型样式
|
// 获取激活用例类型样式
|
||||||
const getActiveClass = (type: string) => {
|
const getActiveClass = (type: string) => {
|
||||||
|
@ -320,181 +530,6 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const websocket = ref<WebSocket>();
|
|
||||||
const temporaryScenarioReportMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 开启websocket监听,接收执行结果
|
|
||||||
*/
|
|
||||||
function debugSocket(reportId?: string | number, executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
|
|
||||||
websocket.value = getSocket(
|
|
||||||
reportId || '',
|
|
||||||
executeType === 'localExec' ? '/ws/debug' : '',
|
|
||||||
executeType === 'localExec' ? localExecuteUrl : ''
|
|
||||||
);
|
|
||||||
websocket.value.addEventListener('message', (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
if (data.msgType === 'EXEC_RESULT') {
|
|
||||||
if (activeScenarioTab.value.reportId === data.reportId) {
|
|
||||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
|
||||||
data.taskResult.requestResults.forEach((result) => {
|
|
||||||
activeScenarioTab.value.stepResponses[result.stepId] = {
|
|
||||||
...result,
|
|
||||||
console: data.taskResult.console,
|
|
||||||
};
|
|
||||||
if (result.isSuccessful) {
|
|
||||||
activeScenarioTab.value.executeSuccessCount += 1;
|
|
||||||
} else {
|
|
||||||
activeScenarioTab.value.executeFailCount += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
|
||||||
data.taskResult.requestResults.forEach((result) => {
|
|
||||||
if (activeScenarioTab.value.reportId) {
|
|
||||||
if (temporaryScenarioReportMap[activeScenarioTab.value.reportId] === undefined) {
|
|
||||||
temporaryScenarioReportMap[activeScenarioTab.value.reportId] = {};
|
|
||||||
}
|
|
||||||
temporaryScenarioReportMap[activeScenarioTab.value.reportId][result.stepId] = {
|
|
||||||
...result,
|
|
||||||
console: data.taskResult.console,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (data.msgType === 'EXEC_END') {
|
|
||||||
// 执行结束,关闭websocket
|
|
||||||
websocket.value?.close();
|
|
||||||
if (activeScenarioTab.value.reportId === data.reportId) {
|
|
||||||
activeScenarioTab.value.executeLoading = false;
|
|
||||||
activeScenarioTab.value.isExecute = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function realExecute(
|
|
||||||
executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId'>,
|
|
||||||
isExecute?: boolean,
|
|
||||||
executeType?: 'localExec' | 'serverExec',
|
|
||||||
localExecuteUrl?: string
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
activeScenarioTab.value.executeLoading = true;
|
|
||||||
debugSocket(executeParams.reportId, executeType, localExecuteUrl); // 开启websocket
|
|
||||||
// 重置执行结果
|
|
||||||
activeScenarioTab.value.executeTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
activeScenarioTab.value.executeSuccessCount = 0;
|
|
||||||
activeScenarioTab.value.executeFailCount = 0;
|
|
||||||
activeScenarioTab.value.stepResponses = {};
|
|
||||||
activeScenarioTab.value.reportId = executeParams.reportId; // 存储报告ID
|
|
||||||
activeScenarioTab.value.isDebug = !isExecute;
|
|
||||||
let res;
|
|
||||||
if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) {
|
|
||||||
// 执行场景且非本地执行且非未保存场景
|
|
||||||
res = await executeScenario({
|
|
||||||
id: activeScenarioTab.value.id,
|
|
||||||
grouped: false,
|
|
||||||
environmentId: currentEnvConfig.value?.id || '',
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
|
||||||
uploadFileIds: activeScenarioTab.value.uploadFileIds,
|
|
||||||
linkFileIds: activeScenarioTab.value.linkFileIds,
|
|
||||||
...executeParams,
|
|
||||||
steps: mapTree(executeParams.steps, (node) => {
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res = await debugScenario({
|
|
||||||
id: activeScenarioTab.value.id,
|
|
||||||
grouped: false,
|
|
||||||
environmentId: currentEnvConfig.value?.id || '',
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
|
||||||
uploadFileIds: activeScenarioTab.value.uploadFileIds,
|
|
||||||
linkFileIds: activeScenarioTab.value.linkFileIds,
|
|
||||||
frontendDebug: executeType === 'localExec',
|
|
||||||
...executeParams,
|
|
||||||
steps: mapTree(executeParams.steps, (node) => {
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
parent: null, // 原树形结构存在循环引用,这里要去掉以免 axios 序列化失败
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (executeType === 'localExec' && localExecuteUrl) {
|
|
||||||
// 本地执行需要调 debug 接口获取响应结果,然后再调本地执行接口
|
|
||||||
await localExecuteApiDebug(localExecuteUrl, res);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
websocket.value?.close();
|
|
||||||
activeScenarioTab.value.executeLoading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleExecute(executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
|
|
||||||
const waitingDebugStepDetails = {};
|
|
||||||
const waitTingDebugSteps = filterTree(activeScenarioTab.value.steps, (node) => {
|
|
||||||
if (node.enable) {
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
|
||||||
waitingDebugStepDetails[node.id] = activeScenarioTab.value.stepDetails[node.id];
|
|
||||||
if (
|
|
||||||
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(node.stepType)
|
|
||||||
) {
|
|
||||||
// 请求和场景类型才直接显示执行中,其他控制器需要等待执行完毕才结算执行结果
|
|
||||||
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !!node.enable;
|
|
||||||
});
|
|
||||||
realExecute(
|
|
||||||
{
|
|
||||||
steps: waitTingDebugSteps,
|
|
||||||
stepDetails: waitingDebugStepDetails,
|
|
||||||
reportId: getGenerateId(),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
executeType,
|
|
||||||
localExecuteUrl
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleStopExecute() {
|
|
||||||
websocket.value?.close();
|
|
||||||
activeScenarioTab.value.executeLoading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => activeScenarioTab.value.id,
|
|
||||||
(val) => {
|
|
||||||
if (val !== 'all' && activeScenarioTab.value.reportId && !activeScenarioTab.value.executeLoading) {
|
|
||||||
// 当前查看的 tab 非全部场景 tab 页,且当前场景有报告ID,且不是正在执行中,则读取缓存报告
|
|
||||||
const cacheReport = temporaryScenarioReportMap[activeScenarioTab.value.reportId];
|
|
||||||
if (cacheReport) {
|
|
||||||
// 如果有缓存的报告未读取,则直接赋值
|
|
||||||
Object.keys(cacheReport).forEach((stepId) => {
|
|
||||||
const result = cacheReport[stepId];
|
|
||||||
activeScenarioTab.value.stepResponses[stepId] = result;
|
|
||||||
if (result.isSuccessful) {
|
|
||||||
activeScenarioTab.value.executeSuccessCount += 1;
|
|
||||||
} else {
|
|
||||||
activeScenarioTab.value.executeFailCount += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
activeScenarioTab.value.executeLoading = false;
|
|
||||||
delete temporaryScenarioReportMap[activeScenarioTab.value.reportId]; // 取完释放缓存
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const isPriorityLocalExec = computed(() => executeButtonRef.value?.isPriorityLocalExec);
|
const isPriorityLocalExec = computed(() => executeButtonRef.value?.isPriorityLocalExec);
|
||||||
const scenarioId = computed(() => activeScenarioTab.value.id);
|
const scenarioId = computed(() => activeScenarioTab.value.id);
|
||||||
const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading);
|
const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading);
|
||||||
|
|
|
@ -162,7 +162,7 @@
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const lastReportStatusFilterVisible = ref(false);
|
const lastReportStatusFilterVisible = ref(false);
|
||||||
const lastReportStatusListFilters = ref<string[]>(Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]));
|
const lastReportStatusListFilters = ref<string[]>([]);
|
||||||
const lastReportStatusFilters = computed(() => {
|
const lastReportStatusFilters = computed(() => {
|
||||||
return Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]);
|
return Object.keys(ReportStatus[ReportEnum.API_SCENARIO_REPORT]);
|
||||||
});
|
});
|
||||||
|
@ -335,7 +335,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref(Object.keys(ApiScenarioStatus));
|
const statusFilters = ref<string[]>([]);
|
||||||
const tableStore = useTableStore();
|
const tableStore = useTableStore();
|
||||||
|
|
||||||
async function loadScenarioList(refreshTreeCount?: boolean) {
|
async function loadScenarioList(refreshTreeCount?: boolean) {
|
||||||
|
@ -354,7 +354,7 @@
|
||||||
moduleIds,
|
moduleIds,
|
||||||
filter: {
|
filter: {
|
||||||
lastReportStatus: lastReportStatusListFilters.value,
|
lastReportStatus: lastReportStatusListFilters.value,
|
||||||
status: statusFilters.value.length === Object.keys(ApiScenarioStatus).length ? undefined : statusFilters.value,
|
status: statusFilters.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
setLoadListParams(params);
|
setLoadListParams(params);
|
||||||
|
|
|
@ -876,9 +876,9 @@
|
||||||
excludeIds: [],
|
excludeIds: [],
|
||||||
currentSelectCount: 0,
|
currentSelectCount: 0,
|
||||||
});
|
});
|
||||||
const statusFilters = ref<string[]>(Object.keys(statusIconMap));
|
const statusFilters = ref<string[]>([]);
|
||||||
const caseFilters = ref<string[]>([]);
|
const caseFilters = ref<string[]>([]);
|
||||||
const executeResultFilters = ref(Object.keys(executionResultMap));
|
const executeResultFilters = ref<string[]>([]);
|
||||||
|
|
||||||
async function initTableParams() {
|
async function initTableParams() {
|
||||||
let moduleIds: string[] = [];
|
let moduleIds: string[] = [];
|
||||||
|
|
|
@ -633,14 +633,14 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// 用例等级表头检索
|
// 用例等级表头检索
|
||||||
const statusFilters = ref<string[]>(Object.keys(statusIconMap));
|
const statusFilters = ref<string[]>([]);
|
||||||
const caseLevelFields = ref<Record<string, any>>({});
|
const caseLevelFields = ref<Record<string, any>>({});
|
||||||
const caseFilterVisible = ref(false);
|
const caseFilterVisible = ref(false);
|
||||||
const caseLevelList = computed(() => {
|
const caseLevelList = computed(() => {
|
||||||
return caseLevelFields.value?.options || [];
|
return caseLevelFields.value?.options || [];
|
||||||
});
|
});
|
||||||
const caseFilters = ref<string[]>([]);
|
const caseFilters = ref<string[]>([]);
|
||||||
const executeResultFilters = ref(Object.keys(executionResultMap));
|
const executeResultFilters = ref<string[]>([]);
|
||||||
const updateUserFilters = ref<string[]>([]);
|
const updateUserFilters = ref<string[]>([]);
|
||||||
const createUserFilters = ref<string[]>([]);
|
const createUserFilters = ref<string[]>([]);
|
||||||
const deleteUserFilters = ref<string[]>([]);
|
const deleteUserFilters = ref<string[]>([]);
|
||||||
|
|
|
@ -240,9 +240,8 @@
|
||||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import { BugListItem, BugOptionItem } from '@/models/bug-management';
|
import { BugOptionItem } from '@/models/bug-management';
|
||||||
import type { TableQueryParams } from '@/models/common';
|
import type { TableQueryParams } from '@/models/common';
|
||||||
import { CommonList } from '@/models/common';
|
|
||||||
|
|
||||||
const featureCaseStore = useFeatureCaseStore();
|
const featureCaseStore = useFeatureCaseStore();
|
||||||
|
|
||||||
|
|
|
@ -473,7 +473,7 @@
|
||||||
async function initFilter() {
|
async function initFilter() {
|
||||||
await featureStore.getDefaultTemplate();
|
await featureStore.getDefaultTemplate();
|
||||||
caseLevelList.value = featureStore.getSystemCaseLevelFields();
|
caseLevelList.value = featureStore.getSystemCaseLevelFields();
|
||||||
caseFilters.value = caseLevelList.value.map((item) => item.value);
|
caseFilters.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -350,7 +350,7 @@
|
||||||
const tableParams = ref<Record<string, any>>({});
|
const tableParams = ref<Record<string, any>>({});
|
||||||
|
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref<string[]>(Object.keys(reviewResultMap));
|
const statusFilters = ref<string[]>([]);
|
||||||
|
|
||||||
const hasOperationPermission = computed(() =>
|
const hasOperationPermission = computed(() =>
|
||||||
hasAnyPermission(['CASE_REVIEW:READ+REVIEW', 'CASE_REVIEW:READ+RELEVANCE'])
|
hasAnyPermission(['CASE_REVIEW:READ+REVIEW', 'CASE_REVIEW:READ+RELEVANCE'])
|
||||||
|
|
|
@ -495,7 +495,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref<string[]>(Object.keys(reviewStatusMap));
|
const statusFilters = ref<string[]>([]);
|
||||||
const tableQueryParams = ref<any>();
|
const tableQueryParams = ref<any>();
|
||||||
async function searchReview(filter?: FilterResult) {
|
async function searchReview(filter?: FilterResult) {
|
||||||
let moduleIds: string[] = [];
|
let moduleIds: string[] = [];
|
||||||
|
|
|
@ -107,7 +107,6 @@
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
|
||||||
import AddScriptDrawer from '@/components/business/ms-common-script/ms-addScriptDrawer.vue';
|
import AddScriptDrawer from '@/components/business/ms-common-script/ms-addScriptDrawer.vue';
|
||||||
import commonScriptStatus from './components/commonScriptStatus.vue';
|
import commonScriptStatus from './components/commonScriptStatus.vue';
|
||||||
import ScriptDetailDrawer from './components/scriptDetailDrawer.vue';
|
import ScriptDetailDrawer from './components/scriptDetailDrawer.vue';
|
||||||
|
@ -129,7 +128,7 @@
|
||||||
ParamsRequestType,
|
ParamsRequestType,
|
||||||
} from '@/models/projectManagement/commonScript';
|
} from '@/models/projectManagement/commonScript';
|
||||||
import { CommonScriptStatusEnum } from '@/enums/commonScriptStatusEnum';
|
import { CommonScriptStatusEnum } from '@/enums/commonScriptStatusEnum';
|
||||||
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||||
|
@ -141,7 +140,7 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const keyword = ref<string>('');
|
const keyword = ref<string>('');
|
||||||
const statusFilters = ref<string[]>(Object.keys(CommonScriptStatusEnum));
|
const statusFilters = ref<string[]>([]);
|
||||||
|
|
||||||
const hasOperationPermission = computed(() =>
|
const hasOperationPermission = computed(() =>
|
||||||
hasAnyPermission(['PROJECT_CUSTOM_FUNCTION:READ+UPDATE', 'PROJECT_CUSTOM_FUNCTION:READ+DELETE'])
|
hasAnyPermission(['PROJECT_CUSTOM_FUNCTION:READ+UPDATE', 'PROJECT_CUSTOM_FUNCTION:READ+DELETE'])
|
||||||
|
|
|
@ -112,7 +112,6 @@
|
||||||
} from '@/api/modules/project-management/taskCenter';
|
} from '@/api/modules/project-management/taskCenter';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import useOpenNewPage from '@/hooks/useOpenNewPage';
|
|
||||||
import { useTableStore } from '@/store';
|
import { useTableStore } from '@/store';
|
||||||
import { characterLimit } from '@/utils';
|
import { characterLimit } from '@/utils';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
@ -135,16 +134,7 @@
|
||||||
}>();
|
}>();
|
||||||
const keyword = ref<string>('');
|
const keyword = ref<string>('');
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusListFilters = ref<string[]>(Object.keys(TaskStatus[props.moduleType]));
|
const statusListFilters = ref<string[]>([]);
|
||||||
const { openNewPage } = useOpenNewPage();
|
|
||||||
const filterOptions = computed(() => {
|
|
||||||
return statusListFilters.value.map((item) => {
|
|
||||||
return {
|
|
||||||
label: item,
|
|
||||||
value: item,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const permissionsMap = {
|
const permissionsMap = {
|
||||||
organization: {
|
organization: {
|
||||||
|
|
|
@ -218,7 +218,7 @@
|
||||||
const keyword = ref<string>('');
|
const keyword = ref<string>('');
|
||||||
const scrollWidth = ref<number>(3400);
|
const scrollWidth = ref<number>(3400);
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
const statusFilters = ref<string[]>(Object.keys(reviewStatusMap));
|
const statusFilters = ref<string[]>([]);
|
||||||
|
|
||||||
const tableBatchActions = {
|
const tableBatchActions = {
|
||||||
baseAction: [
|
baseAction: [
|
||||||
|
|
Loading…
Reference in New Issue