fix(接口测试): csv&场景步骤问题修复
This commit is contained in:
parent
952596f7d7
commit
3c3188e7eb
|
@ -386,6 +386,11 @@ export interface ScenarioStepFileParams {
|
|||
deleteFileIds?: string[];
|
||||
unLinkFileIds?: string[];
|
||||
}
|
||||
// 场景文件参数
|
||||
export interface ScenarioFileParams {
|
||||
uploadFileIds: string[];
|
||||
linkFileIds: string[];
|
||||
}
|
||||
// 场景步骤详情
|
||||
export type ScenarioStepDetails = Partial<RequestParam | CaseRequestParam | ExecuteConditionProcessor>;
|
||||
// 场景
|
||||
|
@ -405,6 +410,7 @@ export interface Scenario {
|
|||
steps: ScenarioStepItem[];
|
||||
stepDetails: Record<string, ScenarioStepDetails>; // case、api、脚本操作抽屉的详情结构
|
||||
stepFileParam: Record<string, ScenarioStepFileParams>;
|
||||
fileParam: ScenarioFileParams;
|
||||
follow?: boolean;
|
||||
uploadFileIds: string[];
|
||||
linkFileIds: string[];
|
||||
|
@ -455,6 +461,7 @@ export interface ApiScenarioDebugRequest {
|
|||
steps: ScenarioStepItem[];
|
||||
projectId: string;
|
||||
stepFileParam: Record<string, ScenarioStepFileParams>;
|
||||
fileParam: ScenarioFileParams;
|
||||
frontendDebug?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -916,7 +916,7 @@
|
|||
(arr) => {
|
||||
if (arr.length > 0) {
|
||||
let hasNoIdItem = false;
|
||||
paramsData.value = arr.map((item, i) => {
|
||||
paramsData.value = arr.map((item) => {
|
||||
if (!item) {
|
||||
// 批量添加过来的数据最后一行会是 undefined
|
||||
hasNoIdItem = true;
|
||||
|
@ -935,7 +935,12 @@
|
|||
}
|
||||
return item;
|
||||
});
|
||||
if (hasNoIdItem && !filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault && !props.isTreeTable) {
|
||||
if (
|
||||
(!props.disabledExceptParam || !props.disabledParamValue) &&
|
||||
hasNoIdItem &&
|
||||
!filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault &&
|
||||
!props.isTreeTable
|
||||
) {
|
||||
addTableLine(arr.length - 1, false, true);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -124,6 +124,7 @@
|
|||
import { CsvVariable } from '@/models/apiTest/scenario';
|
||||
|
||||
import { defaultCsvParamItem } from '@/views/api-test/components/config';
|
||||
import { filterKeyValParams } from '@/views/api-test/components/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
scenarioId?: string | number;
|
||||
|
@ -310,6 +311,18 @@
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (
|
||||
csvVariables.value.length > 0 &&
|
||||
!filterKeyValParams(csvVariables.value, defaultCsvParamItem).lastDataIsDefault
|
||||
) {
|
||||
csvVariables.value.push({
|
||||
...cloneDeep(defaultCsvParamItem),
|
||||
id: getGenerateId(),
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
|
|
@ -409,6 +409,7 @@
|
|||
label: string;
|
||||
name: string;
|
||||
stepId: string | number; // 所属步骤 id
|
||||
uniqueId: string | number; // 前端生成的唯一 id
|
||||
stepName: string; // 所属步骤名称
|
||||
resourceId: string | number; // 引用、复制的资源 id
|
||||
isNew: boolean;
|
||||
|
@ -471,6 +472,7 @@
|
|||
name: '',
|
||||
type: 'api',
|
||||
stepId: '',
|
||||
uniqueId: '',
|
||||
stepName: '',
|
||||
resourceId: '',
|
||||
customizeRequest: true,
|
||||
|
@ -560,7 +562,7 @@
|
|||
);
|
||||
const currentLoop = ref(1);
|
||||
const currentResponse = computed(() => {
|
||||
if (requestVModel.value.stepId === props.step?.uniqueId && props.step?.uniqueId) {
|
||||
if (requestVModel.value.uniqueId === props.step?.uniqueId && props.step?.uniqueId) {
|
||||
// 判断当前步骤 id 与传入的激活步骤 id 是否一致,避免在操作插入步骤时带入的是其他步骤的响应内容
|
||||
return props.stepResponses?.[props.step?.uniqueId]?.[currentLoop.value - 1];
|
||||
}
|
||||
|
@ -574,7 +576,7 @@
|
|||
watch(
|
||||
() => props.stepResponses,
|
||||
(val) => {
|
||||
if (val && val[requestVModel.value.stepId]) {
|
||||
if (val && val[requestVModel.value.uniqueId]) {
|
||||
requestVModel.value.executeLoading = false;
|
||||
}
|
||||
},
|
||||
|
@ -766,7 +768,7 @@
|
|||
const handlePluginFormChange = debounce(() => {
|
||||
if (isEditableApi.value) {
|
||||
// 复制或者新建的时候需要缓存表单数据,引用的不能更改
|
||||
temporaryPluginFormMap[requestVModel.value.stepId] = fApi.value?.formData();
|
||||
temporaryPluginFormMap[requestVModel.value.uniqueId] = fApi.value?.formData();
|
||||
}
|
||||
handleActiveDebugChange();
|
||||
}, 300);
|
||||
|
@ -797,7 +799,7 @@
|
|||
* 设置插件表单数据
|
||||
*/
|
||||
function setPluginFormData() {
|
||||
const tempForm = temporaryPluginFormMap[requestVModel.value.stepId];
|
||||
const tempForm = temporaryPluginFormMap[requestVModel.value.uniqueId];
|
||||
if (tempForm || !requestVModel.value.isNew) {
|
||||
// 如果缓存的表单数据存在或者是编辑状态,则需要将之前的输入数据填充
|
||||
const formData = isEditableApi.value ? tempForm || requestVModel.value : requestVModel.value;
|
||||
|
@ -981,6 +983,7 @@
|
|||
...requestParams,
|
||||
resourceId: requestVModel.value.resourceId,
|
||||
stepId: requestVModel.value.stepId,
|
||||
uniqueId: requestVModel.value.uniqueId,
|
||||
activeTab: requestVModel.value.protocol === 'HTTP' ? RequestComposition.HEADER : RequestComposition.PLUGIN,
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
protocol: requestVModel.value.protocol,
|
||||
|
@ -1153,7 +1156,7 @@
|
|||
activeTab: contentTabList.value[0].value,
|
||||
unSaved: false,
|
||||
isNew: false,
|
||||
label: res.name,
|
||||
label: props.step?.name || res.name,
|
||||
stepName: props.step?.name || res.name,
|
||||
...res.request,
|
||||
...res,
|
||||
|
@ -1162,6 +1165,7 @@
|
|||
name: res.name, // request里面还有个name但是是null
|
||||
resourceId: res.id,
|
||||
stepId: props.step?.id || '',
|
||||
uniqueId: props.step?.uniqueId || '',
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
...parseRequestBodyResult,
|
||||
};
|
||||
|
@ -1214,11 +1218,14 @@
|
|||
* @param newStep 替换的新步骤
|
||||
*/
|
||||
function handleReplace(newStep: ScenarioStepItem) {
|
||||
emit('replace', newStep);
|
||||
emit('replace', {
|
||||
...newStep,
|
||||
name: props.step?.name || newStep.name,
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.request?.stepId,
|
||||
() => props.request?.uniqueId,
|
||||
() => {
|
||||
isSwitchingContent.value = true;
|
||||
},
|
||||
|
@ -1245,6 +1252,7 @@
|
|||
activeTab: contentTabList.value[0].value,
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
stepId: props.step?.uniqueId || '',
|
||||
uniqueId: props.step?.uniqueId || '',
|
||||
isNew: false,
|
||||
});
|
||||
if (_stepType.value.isQuoteApi) {
|
||||
|
@ -1258,9 +1266,11 @@
|
|||
handleActiveDebugProtocolChange(requestVModel.value.protocol);
|
||||
} else {
|
||||
// 新建自定义请求
|
||||
const id = getGenerateId();
|
||||
requestVModel.value = cloneDeep({
|
||||
...defaultApiParams,
|
||||
stepId: getGenerateId(),
|
||||
stepId: id,
|
||||
uniqueId: id,
|
||||
});
|
||||
}
|
||||
requestVModel.value.activeTab = contentTabList.value[0].value;
|
||||
|
|
|
@ -340,6 +340,7 @@
|
|||
stepName: '',
|
||||
type: 'api',
|
||||
stepId: '',
|
||||
uniqueId: '',
|
||||
resourceId: '',
|
||||
customizeRequestEnvEnable: false,
|
||||
protocol: 'HTTP',
|
||||
|
@ -432,7 +433,7 @@
|
|||
watch(
|
||||
() => props.stepResponses,
|
||||
(val) => {
|
||||
if (val && val[requestVModel.value.stepId]) {
|
||||
if (val && val[requestVModel.value.uniqueId]) {
|
||||
requestVModel.value.executeLoading = false;
|
||||
}
|
||||
},
|
||||
|
@ -628,7 +629,7 @@
|
|||
const handlePluginFormChange = debounce(() => {
|
||||
if (isEditableApi.value) {
|
||||
// 复制或者新建的时候需要缓存表单数据,引用的不能更改
|
||||
temporaryPluginFormMap[requestVModel.value.stepId] = fApi.value?.formData();
|
||||
temporaryPluginFormMap[requestVModel.value.uniqueId] = fApi.value?.formData();
|
||||
}
|
||||
handleActiveDebugChange();
|
||||
}, 300);
|
||||
|
@ -659,7 +660,7 @@
|
|||
* 设置插件表单数据
|
||||
*/
|
||||
function setPluginFormData() {
|
||||
const tempForm = temporaryPluginFormMap[requestVModel.value.stepId];
|
||||
const tempForm = temporaryPluginFormMap[requestVModel.value.uniqueId];
|
||||
if (tempForm || !requestVModel.value.isNew) {
|
||||
// 如果缓存的表单数据存在或者是编辑状态,则需要将之前的输入数据填充
|
||||
const formData = isEditableApi.value ? tempForm || requestVModel.value : requestVModel.value;
|
||||
|
@ -820,6 +821,7 @@
|
|||
unSaved: requestVModel.value.unSaved,
|
||||
resourceId: requestVModel.value.resourceId,
|
||||
stepId: requestVModel.value.stepId,
|
||||
uniqueId: requestVModel.value.uniqueId,
|
||||
activeTab: requestVModel.value.protocol === 'HTTP' ? RequestComposition.HEADER : RequestComposition.PLUGIN,
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
protocol: requestVModel.value.protocol,
|
||||
|
@ -977,7 +979,7 @@
|
|||
activeTab: res.protocol === 'HTTP' ? RequestComposition.HEADER : RequestComposition.PLUGIN,
|
||||
unSaved: false,
|
||||
isNew: false,
|
||||
label: res.name,
|
||||
label: activeStep.value?.name || res.name,
|
||||
...res.request,
|
||||
...res,
|
||||
response: cloneDeep(defaultResponse),
|
||||
|
@ -986,6 +988,7 @@
|
|||
name: res.name, // request里面还有个name但是是null
|
||||
resourceId: res.id,
|
||||
stepId: activeStep.value?.id || '',
|
||||
uniqueId: activeStep.value?.uniqueId || '',
|
||||
...parseRequestBodyResult,
|
||||
};
|
||||
nextTick(() => {
|
||||
|
@ -1005,11 +1008,14 @@
|
|||
* @param newStep 替换的新步骤
|
||||
*/
|
||||
function handleReplace(newStep: ScenarioStepItem) {
|
||||
emit('replace', newStep);
|
||||
emit('replace', {
|
||||
...newStep,
|
||||
name: activeStep.value?.name || newStep.name,
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.request?.stepId,
|
||||
() => props.request?.uniqueId,
|
||||
() => {
|
||||
isSwitchingContent.value = true;
|
||||
},
|
||||
|
@ -1031,6 +1037,7 @@
|
|||
...props.request,
|
||||
isNew: false,
|
||||
stepId: props.request?.stepId || '',
|
||||
uniqueId: activeStep.value?.uniqueId || '',
|
||||
stepName: activeStep.value?.name || props.request?.name || '',
|
||||
});
|
||||
if (isQuote.value || isCopyNeedInit.value) {
|
||||
|
|
|
@ -116,6 +116,10 @@ export const defaultScenario: Scenario = {
|
|||
steps: [],
|
||||
stepDetails: {},
|
||||
stepFileParam: {},
|
||||
fileParam: {
|
||||
linkFileIds: [],
|
||||
uploadFileIds: [],
|
||||
},
|
||||
executeTime: 0,
|
||||
executeSuccessCount: 0,
|
||||
executeFailCount: 0,
|
||||
|
|
|
@ -12,7 +12,7 @@ import type { ApiScenarioDebugRequest, Scenario, ScenarioStepItem } from '@/mode
|
|||
import { ScenarioExecuteStatus, ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
|
||||
|
||||
import type { RequestParam } from '../common/customApiDrawer.vue';
|
||||
import updateStepStatus from '../utils';
|
||||
import updateStepStatus, { getScenarioFileParams } from '../utils';
|
||||
|
||||
/**
|
||||
* 步骤执行逻辑
|
||||
|
@ -85,6 +85,9 @@ export default function useStepExecute({
|
|||
projectId: appStore.currentProjectId,
|
||||
scenarioConfig: scenario.value.scenarioConfig,
|
||||
frontendDebug: scenario.value.executeType === 'localExec',
|
||||
fileParam: {
|
||||
...getScenarioFileParams(scenario.value),
|
||||
},
|
||||
...executeParams,
|
||||
steps: mapTree(executeParams.steps, (node) => {
|
||||
return {
|
||||
|
@ -153,7 +156,7 @@ export default function useStepExecute({
|
|||
* @param executeType 执行类型
|
||||
*/
|
||||
function handleApiExecute(request: RequestParam, executeType?: 'localExec' | 'serverExec') {
|
||||
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, request.stepId, 'uniqueId');
|
||||
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, request.uniqueId || request.stepId, 'uniqueId');
|
||||
if (realStep) {
|
||||
delete scenario.value.stepResponses[realStep.uniqueId]; // 先移除上一次的执行结果
|
||||
realStep.reportId = getGenerateId();
|
||||
|
@ -175,7 +178,7 @@ export default function useStepExecute({
|
|||
});
|
||||
} else {
|
||||
// 步骤列表找不到该步骤,说明是新建的自定义请求还未保存,则临时创建一个步骤进行调试(不保存步骤信息)
|
||||
delete scenario.value.stepResponses[request.stepId]; // 先移除上一次的执行结果
|
||||
delete scenario.value.stepResponses[request.uniqueId || request.stepId]; // 先移除上一次的执行结果
|
||||
const reportId = getGenerateId();
|
||||
request.executeLoading = true;
|
||||
activeStep.value = {
|
||||
|
@ -190,7 +193,7 @@ export default function useStepExecute({
|
|||
projectId: appStore.currentProjectId,
|
||||
isExecuting: false,
|
||||
reportId,
|
||||
uniqueId: request.stepId,
|
||||
uniqueId: request.uniqueId,
|
||||
};
|
||||
realExecute({
|
||||
steps: [activeStep.value],
|
||||
|
|
|
@ -96,32 +96,37 @@ export default function useStepOperation({
|
|||
// 复制 api、引用 api、自定义 api打开抽屉
|
||||
activeStep.value = step;
|
||||
if (
|
||||
step.isQuoteScenarioStep ||
|
||||
(stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
|
||||
(stepDetails.value[step.id] === undefined && !step.isNew)
|
||||
) {
|
||||
// 查看场景详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
||||
// 引用的场景步骤资源每次加载最新数据
|
||||
// 查看步骤详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
||||
await getStepDetail(step);
|
||||
}
|
||||
customApiDrawerVisible.value = true;
|
||||
} else if (step.stepType === ScenarioStepType.API_CASE) {
|
||||
activeStep.value = step;
|
||||
if (
|
||||
_stepType.isCopyCase &&
|
||||
((stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
|
||||
(stepDetails.value[step.id] === undefined && !step.isNew))
|
||||
step.isQuoteScenarioStep ||
|
||||
(_stepType.isCopyCase && stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
|
||||
(stepDetails.value[step.id] === undefined && !step.isNew)
|
||||
) {
|
||||
// 引用的场景步骤资源每次加载最新数据
|
||||
// 只有复制的 case 需要查看步骤详情,引用的无法更改所以不需要在此初始化详情
|
||||
// 查看场景详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
||||
// 查看步骤详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
||||
await getStepDetail(step);
|
||||
}
|
||||
customCaseDrawerVisible.value = true;
|
||||
} else if (step.stepType === ScenarioStepType.SCRIPT) {
|
||||
activeStep.value = step;
|
||||
if (
|
||||
step.isQuoteScenarioStep ||
|
||||
(stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
|
||||
(stepDetails.value[step.id] === undefined && !step.isNew)
|
||||
) {
|
||||
// 查看场景详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
||||
// 引用的场景步骤资源每次加载最新数据
|
||||
// 查看步骤详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
||||
await getStepDetail(step);
|
||||
}
|
||||
scriptOperationDrawerVisible.value = true;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { RequestResult } from '@/models/apiTest/common';
|
||||
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||
import { type Scenario, ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||
import { ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
|
||||
|
||||
/**
|
||||
|
@ -94,3 +94,23 @@ export default function updateStepStatus(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景文件参数
|
||||
* @param scenario 场景对象
|
||||
*/
|
||||
export function getScenarioFileParams(scenario: Scenario) {
|
||||
const linkFileIds = new Set<string>();
|
||||
const uploadFileIds = new Set<string>();
|
||||
scenario.scenarioConfig.variable.csvVariables.forEach((item) => {
|
||||
if (item.file.local) {
|
||||
uploadFileIds.add(item.file.fileId);
|
||||
} else if (item.file.fileId) {
|
||||
linkFileIds.add(item.file.fileId);
|
||||
}
|
||||
});
|
||||
return {
|
||||
linkFileIds: Array.from(linkFileIds),
|
||||
uploadFileIds: Array.from(uploadFileIds),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
|
||||
import { defaultCsvParamItem, defaultNormalParamItem } from '../components/config';
|
||||
import { defaultScenario } from './components/config';
|
||||
import updateStepStatus from './components/utils';
|
||||
import updateStepStatus, { getScenarioFileParams } from './components/utils';
|
||||
import {
|
||||
filterAssertions,
|
||||
filterConditionsSqlValidParams,
|
||||
|
@ -263,6 +263,9 @@
|
|||
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
||||
...executeParams,
|
||||
stepFileParam: activeScenarioTab.value.stepFileParam,
|
||||
fileParam: {
|
||||
...getScenarioFileParams(activeScenarioTab.value),
|
||||
},
|
||||
steps: mapTree(executeParams.steps, (node) => {
|
||||
return {
|
||||
...node,
|
||||
|
@ -278,6 +281,9 @@
|
|||
projectId: appStore.currentProjectId,
|
||||
scenarioConfig: activeScenarioTab.value.scenarioConfig,
|
||||
stepFileParam: activeScenarioTab.value.stepFileParam,
|
||||
fileParam: {
|
||||
...getScenarioFileParams(activeScenarioTab.value),
|
||||
},
|
||||
frontendDebug: executeType === 'localExec',
|
||||
...executeParams,
|
||||
steps: mapTree(executeParams.steps, (node) => {
|
||||
|
|
Loading…
Reference in New Issue