feat(接口测试): 联调接口测试接口用例同步合并保存

This commit is contained in:
xinxin.wu 2024-08-08 16:43:59 +08:00 committed by Craftsman
parent 9842103d3e
commit 192a82fff5
12 changed files with 117 additions and 55 deletions

View File

@ -55,6 +55,7 @@ import {
GetModuleTreeUrl,
GetPoolId,
GetPoolOptionUrl,
getSyncedCaseDetailUrl,
GetTrashModuleCountUrl,
GetTrashModuleTreeUrl,
ignoreEveryTimeApiChangeUrl,
@ -125,6 +126,7 @@ import {
DefinitionHistoryItem,
DefinitionHistoryPageParams,
DefinitionReferencePageParams,
diffSyncParams,
EnvModule,
ImportApiDefinitionParams,
mockParams,
@ -330,10 +332,14 @@ export function ignoreEveryTimeChange(id: string, ignore: boolean) {
export function caseTableBatchSync(data: TableQueryParams) {
return MSR.post({ url: caseTableBatchSyncUrl, data });
}
// // 接口测试-接口用例-定义对比用例
// 接口测试-接口用例-定义对比用例
export function diffDataRequest(id: string) {
return MSR.get({ url: `${diffDataUrl}/${id}` });
}
// 接口测试-接口用例-定义对比用例-同步-获取同步后的用例详情
export function getSyncedCaseDetail(data: diffSyncParams) {
return MSR.post({ url: getSyncedCaseDetailUrl, data });
}
/**
* Mock

View File

@ -42,6 +42,7 @@ export const clearThisChangeUrl = '/api/case/api-change/clear'; // 接口定义-
export const caseTableBatchSyncUrl = '/api/case/batch/api-change/sync'; // 接口测试-接口管理-接口用例-批量同步
export const ignoreEveryTimeApiChangeUrl = '/api/case/api-change/ignore'; // 接口测试-接口用例-忽略每次接口变更
export const diffDataUrl = '/api/case/api/compare'; // 接口测试-接口用例-定义对比用例
export const getSyncedCaseDetailUrl = '/api/case/api-change/sync'; // 接口测试-接口用例-定义对比用例-同步-获取同步后的用例详情
/**
* Mock

View File

@ -89,7 +89,7 @@
//
let editor: monaco.editor.IStandaloneCodeEditor;
// diffEditor diffMode
let diffEditor: monaco.editor.IStandaloneDiffEditor;
let diffEditor: monaco.editor.IStandaloneDiffEditor | null;
const codeContainerRef = ref();
// ref
@ -403,12 +403,18 @@
(newDiffMode) => {
if (newDiffMode) {
if (editor) {
editor.dispose();
// DOM
const editorDomNode = editor.getDomNode();
if (editorDomNode && editorDomNode.parentNode) {
editor.dispose();
}
}
initDiffEditor(props.originalValue, props.modelValue);
} else {
if (diffEditor) {
// Error
diffEditor.dispose();
diffEditor = null;
}
init();
}

View File

@ -8,6 +8,7 @@ import {
import { BatchApiParams, ModuleTreeNode, TableQueryParams } from '../common';
import { ExecuteRequestParams, ResponseDefinition } from './common';
import type { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
// 定义-自定义字段
export interface ApiDefinitionCustomField {
@ -419,3 +420,10 @@ export interface batchSyncForm {
syncItems: syncItem;
deleteRedundantParam: boolean;
}
// 对比同步参数
export interface diffSyncParams {
syncItems: syncItem; // 同步项
id: string;
deleteRedundantParam: boolean; // 是否删除多余参数
apiCaseRequest: RequestParam; // 用例详情请求request
}

View File

@ -57,6 +57,7 @@
ref="createAndEditCaseDrawerRef"
v-bind="$attrs"
@load-case="(id)=>getCaseDetailInfo(id as string)"
@show-diff="showDiffDrawer"
/>
<a-divider :margin="0"></a-divider>
<MsTab
@ -88,7 +89,7 @@
:active-defined-id="activeDefinedId"
@close="closeDifferent"
@clear-this-change="clearThisChangeHandler"
@sync="syncHandler"
@sync="syncParamsHandler"
/>
</div>
<tab-case-dependency v-else-if="activeKey === 'reference'" :source-id="caseDetail.id" />
@ -404,9 +405,10 @@
getCaseDetailInfo(caseId.value);
}
function syncHandler(id: string) {
// TODO
createAndEditCaseDrawerRef.value?.open(id, caseDetail.value, false);
//
async function syncParamsHandler(mergedRequestParam: RequestParam) {
caseDetail.value = { ...caseDetail.value, ...mergedRequestParam };
createAndEditCaseDrawerRef.value?.open(caseDetail.value.apiDefinitionId, caseDetail.value, false);
}
onBeforeMount(() => {

View File

@ -74,7 +74,7 @@
:active-defined-id="activeDefinedId"
@close="closeDifferent"
@clear-this-change="clearThisChangeHandler"
@sync="syncHandler"
@sync="syncParamsHandler"
/>
</a-tab-pane>
<a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]">
@ -351,9 +351,10 @@
emit('loadCase', props.detail.id as string);
}
function syncHandler(id: string) {
// TODO
createAndEditCaseDrawerRef.value?.open(id, caseDetail.value, false);
//
function syncParamsHandler(mergedRequestParam: RequestParam) {
caseDetail.value = { ...caseDetail.value, ...mergedRequestParam };
createAndEditCaseDrawerRef.value?.open(caseDetail.value.apiDefinitionId, caseDetail.value, false);
}
watch(

View File

@ -315,7 +315,7 @@
:active-defined-id="activeDefinedId"
@close="closeDifferent"
@clear-this-change="handleClearThisChange"
@sync="syncHandler"
@sync="syncParamsHandler"
/>
</template>
@ -1034,10 +1034,12 @@
}
//
function syncHandler(definedId: string) {
// TODO
createAndEditCaseDrawerRef.value?.open(definedId, caseDetail.value as RequestParam, false);
async function syncParamsHandler(mergedRequestParam: RequestParam) {
await getCaseDetailInfo(activeApiCaseId.value);
caseDetail.value = { ...caseDetail.value, ...mergedRequestParam };
createAndEditCaseDrawerRef.value?.open(caseDetail.value.apiDefinitionId, caseDetail.value as RequestParam, false);
}
defineExpose({
loadCaseList,
});

View File

@ -1,6 +1,6 @@
<template>
<div v-for="item of requestBodyList" :key="item.value">
<div class="grid grid-cols-2 gap-[24px]">
<div v-if="getBodyDefinedCode(item.value) || getBodyCaseCode(item.value)" class="grid grid-cols-2 gap-[24px]">
<div class="title">
{{ item.title }}
</div>
@ -47,7 +47,10 @@
{{ t('case.notSetData') }}
</div>
</div>
<div v-else class="grid grid-cols-2 gap-[24px]">
<div
v-if="!getShowDiffer(item.value) && (getBodyDefinedCode(item.value) || getBodyCaseCode(item.value))"
class="grid grid-cols-2 gap-[24px]"
>
<div
v-if="!getBodyCaseCode(item.value) && !getBodyDefinedCode(item.value)"
class="no-json-case-data no-case-data"

View File

@ -13,12 +13,11 @@
<div class="flex w-full items-center justify-between">
<div>{{ t('case.apiAndCaseDiff') }}</div>
<div class="flex items-center text-[14px]">
<div class="-mt-[2px] mr-[8px]"> {{ t('case.syncItem') }}</div>
<a-checkbox-group v-model="checkType">
<div v-if="caseDetail.inconsistentWithApi" class="-mt-[2px] mr-[8px]"> {{ t('case.syncItem') }}</div>
<a-checkbox-group v-if="caseDetail.inconsistentWithApi" v-model="checkType">
<a-checkbox v-for="item of checkList" :key="item.value" :value="item.value">
<div class="flex items-center"
>{{ item.label }}
<div class="flex items-center">
{{ item.label }}
<a-tooltip v-if="item.tooltip" :content="item.tooltip" position="top">
<div class="flex items-center">
<icon-question-circle
@ -30,7 +29,7 @@
</div>
</a-checkbox>
</a-checkbox-group>
<a-divider direction="vertical" :margin="0" class="!mr-[8px]"></a-divider>
<a-divider v-if="caseDetail.inconsistentWithApi" direction="vertical" :margin="0" class="!mr-[8px]" />
<a-switch
v-model:model-value="form.ignoreApiChange"
:before-change="(val) => changeIgnore(val)"
@ -38,28 +37,41 @@
/>
<div class="ml-[8px]">{{ t('case.ignoreAllChange') }}</div>
<a-divider direction="vertical" :margin="8"></a-divider>
<a-switch v-model:model-value="form.deleteRedundantParam" size="small" />
<div class="ml-[8px] font-normal text-[var(--color-text-1)]">{{ t('case.deleteNotCorrespondValue') }}</div>
<a-switch
v-if="caseDetail.inconsistentWithApi"
v-model:model-value="form.deleteRedundantParam"
size="small"
/>
<div v-if="caseDetail.inconsistentWithApi" class="ml-[8px] font-normal text-[var(--color-text-1)]">{{
t('case.deleteNotCorrespondValue')
}}</div>
<a-divider direction="vertical" :margin="0" class="!ml-[8px]"></a-divider>
<a-button class="mx-[12px]" type="secondary" @click="cancel">{{ t('common.cancel') }}</a-button>
<a-button class="mr-[12px]" type="outline" @click="clearThisChangeHandler">
<a-button
v-if="caseDetail.inconsistentWithApi"
class="mr-[12px]"
type="outline"
@click="clearThisChangeHandler"
>
{{ t('case.ignoreThisChange') }}
</a-button>
<a-button type="primary" :loading="syncLoading" :disabled="!checkType.length" @click="confirmSync">
{{ t('case.apiSyncChange') }}
{{ caseDetail.inconsistentWithApi ? t('case.apiSyncChange') : t('common.confirm') }}
</a-button>
</div>
</div>
</template>
<!-- 图例 -->
<div class="legend-container">
<div class="item mr-[24px]">
<div class="legend add"></div>
{{ t('case.diffAdd') }}
</div>
<div class="item">
<div class="legend delete"></div>
{{ t('common.delete') }}
<div class="flex items-center">
<div class="item mr-[8px]">
<div class="legend add"></div>
{{ t('case.diffAdd') }}
</div>
<div class="item">
<div class="legend delete"></div>
{{ t('common.delete') }}
</div>
</div>
</div>
<!-- 对比 -->
@ -81,7 +93,6 @@
<DiffItem :diff-distance-map="diffDistanceMap" mode="delete" :detail="caseDetail as RequestParam" />
</div>
</div>
<DiffRequestBody
:defined-detail="apiDefinedRequest as RequestParam"
:case-detail="caseDetail as RequestParam"
@ -108,12 +119,13 @@
diffDataRequest,
getCaseDetail,
getDefinitionDetail,
getSyncedCaseDetail,
ignoreEveryTimeChange,
} from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
import { EnableKeyValueParam, ExecuteRequestCommonParam } from '@/models/apiTest/common';
import type { syncItem } from '@/models/apiTest/management';
import type { diffSyncParams, syncItem } from '@/models/apiTest/management';
import { ApiDefinitionDetail } from '@/models/apiTest/management';
import { RequestBodyFormat, RequestComposition } from '@/enums/apiEnum';
@ -130,7 +142,7 @@
const emit = defineEmits<{
(e: 'close'): void;
(e: 'clearThisChange'): void;
(e: 'sync', activeDefinedId: string): void;
(e: 'sync', mergeRequest: RequestParam): void;
}>();
const showDiffVisible = defineModel<boolean>('visible', {
@ -165,8 +177,6 @@
query: false,
rest: false,
},
noticeApiCaseCreator: true,
noticeApiScenarioCreator: true,
ignoreApiChange: false,
};
@ -175,21 +185,13 @@
const form = ref({ ...initForm });
function cancel() {
form.value = { ...initForm };
checkType.value = [];
showDiffVisible.value = false;
emit('close');
}
const syncLoading = ref<boolean>(false);
//
function confirmSync() {
//
checkType.value.forEach((e: any) => {
const key = e.toLowerCase() as keyof syncItem;
form.value.syncItems[key] = true;
});
emit('sync', props.activeDefinedId);
showDiffVisible.value = false;
}
const syncLoading = ref<boolean>(false);
const defaultCaseParams = inject<RequestParam>('defaultCaseParams');
const caseDetail = ref<Record<string, any>>({});
@ -318,10 +320,43 @@
getBodyData(RequestBodyFormat.FORM_DATA);
getBodyData(RequestBodyFormat.WWW_FORM);
}
const syncCaseDetail = ref<Record<string, any>>({});
//
async function confirmSync() {
if (!caseDetail.value.inconsistentWithApi) {
cancel();
return;
}
syncLoading.value = true;
try {
//
checkType.value.forEach((e: any) => {
const key = e.toLowerCase() as keyof syncItem;
form.value.syncItems[key] = true;
});
const { id, request } = syncCaseDetail.value;
const params: diffSyncParams = {
...form.value,
apiCaseRequest: request,
id,
};
const mergeRequest = await getSyncedCaseDetail(params);
emit('sync', mergeRequest);
cancel();
} catch (error) {
console.log(error);
} finally {
syncLoading.value = false;
}
}
//
async function getCaseDetailInfo(id: string) {
try {
const res = await getCaseDetail(id);
syncCaseDetail.value = res;
const result = await diffDataRequest(id);
const { caseRequest, apiRequest } = result;
caseDetail.value = {

View File

@ -279,9 +279,8 @@
watch(
[() => configList.value, () => cardItemList.value],
() => {
const configValue = resetConfigEditList(configList.value);
const cardItemValue = resetConfigEditList(cardItemList.value);
const configValue = resetConfigEditList(cloneDeep(configList.value));
const cardItemValue = resetConfigEditList(cloneDeep(cardItemList.value));
const isisEqualList = props.isGroup ? cloneDeep(defaultGroupConfig) : cloneDeep(defaultSingleConfig);
if (!isEqual(configValue, isisEqualList) || (!isEqual(cardItemValue, isisEqualList) && !isInit.value)) {
if (isInit.value) {

View File

@ -616,7 +616,6 @@
...currentItem,
...formValue,
};
innerCardList.value = innerCardList.value.map((item: configItem) => {
if (item.id === currentItem.id) {
return {

View File

@ -61,7 +61,7 @@
apiBugCount: 0,
scenarioBugCount: 0,
testPlanName: '',
defaultLayout: true,
defaultLayout: false,
});
const isGroup = computed(() => route.query.type === 'GROUP');