fix(接口管理): 用例执行渲染出响应数据
This commit is contained in:
parent
d85c5d3ee5
commit
9b0a28ac9f
|
@ -234,26 +234,26 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-collapse-item>
|
</a-collapse-item>
|
||||||
<a-spin :loading="previewDetail.executeLoading" class="w-full">
|
<a-collapse-item
|
||||||
<a-collapse-item
|
v-if="
|
||||||
v-if="
|
previewDetail.responseDefinition &&
|
||||||
previewDetail.responseDefinition &&
|
previewDetail.responseDefinition.length > 0 &&
|
||||||
previewDetail.responseDefinition.length > 0 &&
|
props.detail.protocol === 'HTTP'
|
||||||
props.detail.protocol === 'HTTP'
|
"
|
||||||
"
|
key="response"
|
||||||
key="response"
|
>
|
||||||
>
|
<template #header>
|
||||||
<template #header>
|
<div class="flex items-center gap-[4px]">
|
||||||
<div class="flex items-center gap-[4px]">
|
<div v-if="activeDetailKey.includes('response')" class="down-icon">
|
||||||
<div v-if="activeDetailKey.includes('response')" class="down-icon">
|
<icon-down :size="10" class="block" />
|
||||||
<icon-down :size="10" class="block" />
|
|
||||||
</div>
|
|
||||||
<div v-else class="h-[16px] w-[16px] !rounded-full p-[4px]">
|
|
||||||
<icon-right :size="10" class="block" />
|
|
||||||
</div>
|
|
||||||
<div class="font-medium">{{ t('apiTestManagement.responseContent') }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<div v-else class="h-[16px] w-[16px] !rounded-full p-[4px]">
|
||||||
|
<icon-right :size="10" class="block" />
|
||||||
|
</div>
|
||||||
|
<div class="font-medium">{{ t('apiTestManagement.responseContent') }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="!props.isCase">
|
||||||
<MsEditableTab
|
<MsEditableTab
|
||||||
v-model:active-tab="activeResponse"
|
v-model:active-tab="activeResponse"
|
||||||
:tabs="previewDetail.responseDefinition?.map((e) => ({ ...e, closable: false })) || []"
|
:tabs="previewDetail.responseDefinition?.map((e) => ({ ...e, closable: false })) || []"
|
||||||
|
@ -313,8 +313,23 @@
|
||||||
</div>
|
</div>
|
||||||
<MsFormTable :columns="responseHeaderColumns" :data="activeResponse?.headers || []" :selectable="false" />
|
<MsFormTable :columns="responseHeaderColumns" :data="activeResponse?.headers || []" :selectable="false" />
|
||||||
</div>
|
</div>
|
||||||
</a-collapse-item>
|
</template>
|
||||||
</a-spin>
|
<a-spin v-else :loading="previewDetail.executeLoading" class="h-[calc(100%-45px)] w-full pb-[18px]">
|
||||||
|
<result
|
||||||
|
v-show="
|
||||||
|
previewDetail.protocol === 'HTTP' || previewDetail.response?.requestResults[0]?.responseResult.responseCode
|
||||||
|
"
|
||||||
|
v-model:active-tab="previewDetail.responseActiveTab"
|
||||||
|
:request-result="previewDetail.response?.requestResults[0]"
|
||||||
|
:console="previewDetail.response?.console"
|
||||||
|
:is-http-protocol="previewDetail.protocol === 'HTTP'"
|
||||||
|
:is-priority-local-exec="props.isPriorityLocalExec"
|
||||||
|
:request-url="previewDetail.url"
|
||||||
|
is-definition
|
||||||
|
@execute="emit('execute', props.isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
||||||
|
/>
|
||||||
|
</a-spin>
|
||||||
|
</a-collapse-item>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -330,6 +345,7 @@
|
||||||
import MsFormTable, { FormTableColumn } from '@/components/pure/ms-form-table/index.vue';
|
import MsFormTable, { FormTableColumn } from '@/components/pure/ms-form-table/index.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import { ResponseItem } from '@/views/api-test/components/requestComposition/response/edit.vue';
|
import { ResponseItem } from '@/views/api-test/components/requestComposition/response/edit.vue';
|
||||||
|
import result from '@/views/api-test/components/requestComposition/response/result.vue';
|
||||||
|
|
||||||
import { getPluginScript } from '@/api/modules/api-test/common';
|
import { getPluginScript } from '@/api/modules/api-test/common';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
@ -343,6 +359,10 @@
|
||||||
isCase?: boolean; // case详情
|
isCase?: boolean; // case详情
|
||||||
detail: RequestParam;
|
detail: RequestParam;
|
||||||
protocols: ProtocolItem[];
|
protocols: ProtocolItem[];
|
||||||
|
isPriorityLocalExec?: boolean;
|
||||||
|
}>();
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'execute', val: 'localExec' | 'serverExec'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
|
@ -4,7 +4,12 @@
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<div class="flex gap-[12px]">
|
<div class="flex gap-[12px]">
|
||||||
<environmentSelect v-if="props.isDrawer" ref="environmentSelectRef" />
|
<environmentSelect v-if="props.isDrawer" ref="environmentSelectRef" />
|
||||||
<execute v-model:detail="caseDetail" :environment-id="environmentId as string" />
|
<execute
|
||||||
|
ref="executeRef"
|
||||||
|
v-model:detail="caseDetail"
|
||||||
|
:environment-id="environmentId as string"
|
||||||
|
is-case-detail
|
||||||
|
/>
|
||||||
<a-dropdown position="br" :hide-on-select="false" @select="handleSelect">
|
<a-dropdown position="br" :hide-on-select="false" @select="handleSelect">
|
||||||
<a-button v-if="!props.isDrawer">{{ t('common.operation') }}</a-button>
|
<a-button v-if="!props.isDrawer">{{ t('common.operation') }}</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
|
@ -45,7 +50,13 @@
|
||||||
<caseLevel :case-level="value as CaseLevel" />
|
<caseLevel :case-level="value as CaseLevel" />
|
||||||
</template>
|
</template>
|
||||||
</MsDetailCard>
|
</MsDetailCard>
|
||||||
<detailTab :detail="caseDetail" :protocols="protocols as ProtocolItem[]" is-case />
|
<detailTab
|
||||||
|
:detail="caseDetail"
|
||||||
|
:protocols="protocols as ProtocolItem[]"
|
||||||
|
:is-priority-local-exec="isPriorityLocalExec"
|
||||||
|
is-case
|
||||||
|
@execute="(val: 'localExec' | 'serverExec')=>executeRef?.execute(val)"
|
||||||
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]">
|
<a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]">
|
||||||
<tab-case-dependency :source-id="caseDetail.id" />
|
<tab-case-dependency :source-id="caseDetail.id" />
|
||||||
|
@ -213,6 +224,9 @@
|
||||||
props.isDrawer ? currentEnvConfigByDrawer.value?.id : currentEnvConfigByInject?.value?.id
|
props.isDrawer ? currentEnvConfigByDrawer.value?.id : currentEnvConfigByInject?.value?.id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const executeRef = ref<InstanceType<typeof execute>>();
|
||||||
|
const isPriorityLocalExec = computed(() => executeRef.value?.isPriorityLocalExec ?? false);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
editCase,
|
editCase,
|
||||||
share,
|
share,
|
||||||
|
@ -248,4 +262,7 @@
|
||||||
color: rgb(var(--danger-6));
|
color: rgb(var(--danger-6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
:deep(.monaco-editor) {
|
||||||
|
height: 212px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
@select="execute"
|
@select="execute"
|
||||||
>
|
>
|
||||||
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
|
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
|
||||||
<template v-if="hasLocalExec" #icon>
|
<template #icon>
|
||||||
<icon-down />
|
<icon-down />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="hasLocalExec" #content>
|
<template #content>
|
||||||
<a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
|
<a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
|
||||||
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
|
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
|
||||||
</a-doption>
|
</a-doption>
|
||||||
|
@ -28,15 +28,17 @@
|
||||||
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
||||||
import { debugCase, runCase } from '@/api/modules/api-test/management';
|
import { debugCase, runCase } from '@/api/modules/api-test/management';
|
||||||
import { getSocket } from '@/api/modules/project-management/commonScript';
|
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||||
import { getLocalConfig } from '@/api/modules/user/index';
|
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import { getGenerateId } from '@/utils';
|
import { getGenerateId } from '@/utils';
|
||||||
|
|
||||||
|
import { LocalConfig } from '@/models/user';
|
||||||
|
|
||||||
import { defaultResponse } from '@/views/api-test/components/config';
|
import { defaultResponse } from '@/views/api-test/components/config';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
request?: (...args) => Record<string, any>;
|
request?: (...args) => Record<string, any>;
|
||||||
|
isCaseDetail?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -46,31 +48,13 @@
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasLocalExec = ref(false); // 是否配置了api本地执行
|
const apiLocalExec = inject<Ref<LocalConfig>>('apiLocalExec');
|
||||||
const isPriorityLocalExec = ref(false); // 是否优先本地执行
|
const isPriorityLocalExec = ref(apiLocalExec?.value.enable || false); // 是否优先本地执行
|
||||||
const localExecuteUrl = ref('');
|
const localExecuteUrl = ref(apiLocalExec?.value.userUrl || '');
|
||||||
const reportId = ref('');
|
const reportId = ref('');
|
||||||
const websocket = ref<WebSocket>();
|
const websocket = ref<WebSocket>();
|
||||||
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||||
|
|
||||||
async function initLocalConfig() {
|
|
||||||
if (hasLocalExec.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const res = await getLocalConfig(); // TODO: 会报错
|
|
||||||
const apiLocalExec = res.find((e) => e.type === 'API');
|
|
||||||
if (apiLocalExec) {
|
|
||||||
hasLocalExec.value = true;
|
|
||||||
isPriorityLocalExec.value = apiLocalExec.enable || false;
|
|
||||||
localExecuteUrl.value = apiLocalExec.userUrl || '';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开启websocket监听,接收执行结果
|
* 开启websocket监听,接收执行结果
|
||||||
*/
|
*/
|
||||||
|
@ -85,8 +69,7 @@
|
||||||
if (data.msgType === 'EXEC_RESULT') {
|
if (data.msgType === 'EXEC_RESULT') {
|
||||||
if (caseDetail.value.reportId === data.reportId) {
|
if (caseDetail.value.reportId === data.reportId) {
|
||||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||||
// TODO: 渲染出用例详情的响应数据
|
caseDetail.value.response = data.taskResult; // 渲染出用例详情和创建用例抽屉的响应数据
|
||||||
caseDetail.value.response = data.taskResult; // 渲染出创建用例抽屉的响应数据
|
|
||||||
caseDetail.value.executeLoading = false;
|
caseDetail.value.executeLoading = false;
|
||||||
} else {
|
} else {
|
||||||
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
||||||
|
@ -114,28 +97,28 @@
|
||||||
reportId: reportId.value,
|
reportId: reportId.value,
|
||||||
};
|
};
|
||||||
debugSocket(executeType); // 开启websocket
|
debugSocket(executeType); // 开启websocket
|
||||||
if ((caseDetail.value.id as string).startsWith('c')) {
|
if (!(caseDetail.value.id as string).startsWith('c') && executeType === 'serverExec') {
|
||||||
// 还没创建
|
// 已创建的服务端
|
||||||
res = await debugCase({
|
|
||||||
request: makeRequestParams?.request,
|
|
||||||
linkFileIds: makeRequestParams?.linkFileIds,
|
|
||||||
uploadFileIds: makeRequestParams?.uploadFileIds,
|
|
||||||
id: `case-${Date.now()}`,
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
...params,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res = await runCase({
|
res = await runCase({
|
||||||
request: caseDetail.value.request,
|
request: props.isCaseDetail ? caseDetail.value.request : makeRequestParams?.request,
|
||||||
id: caseDetail.value.id as string,
|
id: caseDetail.value.id as string,
|
||||||
projectId: caseDetail.value.projectId,
|
projectId: caseDetail.value.projectId,
|
||||||
linkFileIds: caseDetail.value.linkFileIds,
|
linkFileIds: caseDetail.value.linkFileIds,
|
||||||
uploadFileIds: caseDetail.value.uploadFileIds,
|
uploadFileIds: caseDetail.value.uploadFileIds,
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
res = await debugCase({
|
||||||
|
request: props.isCaseDetail ? caseDetail.value.request : makeRequestParams?.request,
|
||||||
|
linkFileIds: makeRequestParams?.linkFileIds,
|
||||||
|
uploadFileIds: makeRequestParams?.uploadFileIds,
|
||||||
|
id: `case-${Date.now()}`,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (executeType === 'localExec') {
|
if (executeType === 'localExec') {
|
||||||
await localExecuteApiDebug(localExecuteUrl.value, res); // TODO: 会报错
|
await localExecuteApiDebug(localExecuteUrl.value, res);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
@ -149,8 +132,9 @@
|
||||||
caseDetail.value.executeLoading = false;
|
caseDetail.value.executeLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
defineExpose({
|
||||||
initLocalConfig();
|
isPriorityLocalExec,
|
||||||
|
execute,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
|
|
||||||
// import MockTable from '@/views/api-test/management/components/management/mock/mockTable.vue';
|
// import MockTable from '@/views/api-test/management/components/management/mock/mockTable.vue';
|
||||||
import { getEnvironment, getEnvList, getProtocolList } from '@/api/modules/api-test/common';
|
import { getEnvironment, getEnvList, getProtocolList } from '@/api/modules/api-test/common';
|
||||||
|
import { getLocalConfig } from '@/api/modules/user/index';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
@ -77,6 +78,7 @@
|
||||||
import { ProtocolItem } from '@/models/apiTest/common';
|
import { ProtocolItem } from '@/models/apiTest/common';
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||||
|
import { LocalConfig } from '@/models/user';
|
||||||
import {
|
import {
|
||||||
RequestAuthType,
|
RequestAuthType,
|
||||||
RequestComposition,
|
RequestComposition,
|
||||||
|
@ -328,15 +330,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const apiLocalExec = ref<Record<string, any> | LocalConfig | undefined>({});
|
||||||
|
async function initLocalConfig() {
|
||||||
|
try {
|
||||||
|
const res = await getLocalConfig(); // TODO: 会报错
|
||||||
|
apiLocalExec.value = res.find((e) => e.type === 'API');
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
initEnvList();
|
initEnvList();
|
||||||
initProtocolList();
|
initProtocolList();
|
||||||
|
initLocalConfig();
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 向孙组件提供属性 */
|
/** 向孙组件提供属性 */
|
||||||
provide('currentEnvConfig', readonly(currentEnvConfig));
|
provide('currentEnvConfig', readonly(currentEnvConfig));
|
||||||
provide('defaultCaseParams', readonly(defaultCaseParams));
|
provide('defaultCaseParams', readonly(defaultCaseParams));
|
||||||
provide('protocols', readonly(protocols));
|
provide('protocols', readonly(protocols));
|
||||||
|
provide('apiLocalExec', readonly(apiLocalExec));
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
newTab,
|
newTab,
|
||||||
|
|
Loading…
Reference in New Issue