fix(接口管理): 用例执行渲染出响应数据

This commit is contained in:
teukkk 2024-03-19 21:03:17 +08:00 committed by Craftsman
parent d85c5d3ee5
commit 9b0a28ac9f
4 changed files with 100 additions and 64 deletions

View File

@ -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();

View File

@ -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>

View File

@ -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 = {}; // websockettab const temporaryResponseMap = {}; // websockettab
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) {
// tabtab // tabtab
// 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>

View File

@ -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,