feat(接口测试): 接口测试diff对比同步以及状态接口联调部分
This commit is contained in:
parent
5bdbb92b14
commit
a7a50d1516
|
@ -20,7 +20,9 @@ import {
|
|||
BatchRecoverCaseUrl,
|
||||
BatchUpdateDefinitionUrl,
|
||||
CasePageUrl,
|
||||
caseTableBatchSyncUrl,
|
||||
CheckDefinitionScheduleUrl,
|
||||
clearThisChangeUrl,
|
||||
ConvertJsonSchemaToJsonUrl,
|
||||
CopyMockUrl,
|
||||
DebugCaseUrl,
|
||||
|
@ -35,6 +37,7 @@ import {
|
|||
DeleteModuleUrl,
|
||||
DeleteRecycleApiUrl,
|
||||
DeleteRecycleCaseUrl,
|
||||
diffDataUrl,
|
||||
ExecuteCaseUrl,
|
||||
ExportDefinitionUrl,
|
||||
GetCaseDetailUrl,
|
||||
|
@ -54,6 +57,7 @@ import {
|
|||
GetPoolOptionUrl,
|
||||
GetTrashModuleCountUrl,
|
||||
GetTrashModuleTreeUrl,
|
||||
ignoreEveryTimeApiChangeUrl,
|
||||
ImportDefinitionUrl,
|
||||
JsonSchemaAutoGenerateUrl,
|
||||
MockDetailUrl,
|
||||
|
@ -137,6 +141,7 @@ import {
|
|||
MoveModules,
|
||||
TransferFileParams,
|
||||
} from '@/models/common';
|
||||
import { TableQueryParams } from '@/models/common';
|
||||
import { ResourcePoolItem } from '@/models/setting/resourcePool';
|
||||
|
||||
// 更新模块
|
||||
|
@ -313,6 +318,22 @@ export function convertJsonSchemaToJson(data: JsonSchema) {
|
|||
export function jsonSchemaAutoGenerate(data: JsonSchema) {
|
||||
return MSR.post({ url: JsonSchemaAutoGenerateUrl, data });
|
||||
}
|
||||
// 接口定义-用例接口对比-清除本次变更
|
||||
export function clearThisChange(id: string) {
|
||||
return MSR.get({ url: `${clearThisChangeUrl}/${id}` });
|
||||
}
|
||||
// 接口定义-用例接口对比-忽略每次变更
|
||||
export function ignoreEveryTimeChange(id: string, ignore: boolean) {
|
||||
return MSR.get({ url: `${ignoreEveryTimeApiChangeUrl}/${id}`, params: { ignore } });
|
||||
}
|
||||
// 接口测试-接口管理-接口用例-批量同步编辑
|
||||
export function caseTableBatchSync(data: TableQueryParams) {
|
||||
return MSR.post({ url: caseTableBatchSyncUrl, data });
|
||||
}
|
||||
// // 接口测试-接口用例-定义对比用例
|
||||
export function diffDataRequest(id: string) {
|
||||
return MSR.get({ url: `${diffDataUrl}/${id}` });
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock
|
||||
|
|
|
@ -38,6 +38,10 @@ export const RecoverOperationHistoryUrl = '/api/definition/operation-history/rec
|
|||
export const DefinitionReferenceUrl = '/api/definition/get-reference'; // 获取接口引用关系
|
||||
export const ConvertJsonSchemaToJsonUrl = '/api/definition/json-schema/preview'; // 将json-schema转换为 json 数据
|
||||
export const JsonSchemaAutoGenerateUrl = '/api/definition/json-schema/auto-generate'; // 将json-schema转换为 json 数据
|
||||
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'; // 接口测试-接口用例-定义对比用例
|
||||
|
||||
/**
|
||||
* Mock
|
||||
|
|
|
@ -549,7 +549,7 @@
|
|||
return totalWidth + tablePadding;
|
||||
};
|
||||
|
||||
// 求总和里边最大宽度作为标签列宽
|
||||
// TODO 求总和里边最大宽度作为标签列宽 这里需要考虑一下性能优化
|
||||
const getMaxRowTagWidth = (rows: TableData[], dataIndex: string) => {
|
||||
const allTags = ((rows as TableData) || []).map((row: TableData) => row[dataIndex] || []);
|
||||
|
||||
|
|
|
@ -328,6 +328,8 @@ export interface ApiCaseDetail extends ExecuteRequestParams {
|
|||
deleteTime: number;
|
||||
deleteUser: string;
|
||||
deleteName: string;
|
||||
apiChange: boolean; // 接口定义参数变更标识
|
||||
inconsistentWithApi: boolean; // 与接口定义不一致
|
||||
}
|
||||
// 批量操作参数
|
||||
export interface ApiCaseBatchParams extends BatchApiParams {
|
||||
|
@ -401,3 +403,19 @@ export interface ApiCaseExecuteHistoryItem {
|
|||
triggerMode: string;
|
||||
deleted: boolean;
|
||||
}
|
||||
export interface syncItem {
|
||||
header: boolean;
|
||||
body: boolean;
|
||||
query: boolean;
|
||||
rest: boolean;
|
||||
}
|
||||
// 批量同步
|
||||
export interface batchSyncForm {
|
||||
notificationConfig: {
|
||||
apiCreator: boolean;
|
||||
scenarioCreator: boolean;
|
||||
};
|
||||
// 同步项目
|
||||
syncItems: syncItem;
|
||||
deleteRedundantParam: boolean;
|
||||
}
|
||||
|
|
|
@ -1002,7 +1002,6 @@
|
|||
() => requestMethodsOptions.value,
|
||||
() => {
|
||||
initFilterColumn();
|
||||
apiTableRef.value.initColumn(columns);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -12,19 +12,12 @@
|
|||
<icon-right :size="10" class="block" />
|
||||
</div>
|
||||
</div>
|
||||
<MsTag
|
||||
v-if="props.detail.inconsistentWithApi"
|
||||
class="cursor-pointer"
|
||||
type="warning"
|
||||
theme="light"
|
||||
:tooltip-disabled="true"
|
||||
@click.stop="showDiffDrawer"
|
||||
>
|
||||
<template #icon>
|
||||
<MsIcon type="icon-icon_warning_colorful" size="16" />
|
||||
</template>
|
||||
<span class="ml-[8px]"> {{ statusText }}</span>
|
||||
</MsTag>
|
||||
<ApiChangeTag
|
||||
:ignore-api-change="props.detail.ignoreApiChange"
|
||||
:ignore-api-diff="props.detail.ignoreApiDiff"
|
||||
:inconsistent-with-api="props.detail.inconsistentWithApi"
|
||||
@show-diff="showDiffDrawer"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="detail-collapse-item">
|
||||
|
@ -435,10 +428,10 @@
|
|||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsJsonSchema from '@/components/pure/ms-json-schema/index.vue';
|
||||
import { parseSchemaToJsonSchemaTableData } from '@/components/pure/ms-json-schema/utils';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import { ResponseItem } from '@/views/api-test/components/requestComposition/response/edit.vue';
|
||||
import responseCodeTimeSize from '@/views/api-test/components/requestComposition/response/responseCodeTimeSize.vue';
|
||||
import Result from '@/views/api-test/components/requestComposition/response/result.vue';
|
||||
import ApiChangeTag from '@/views/api-test/management/components/management/case/apiChangeTag.vue';
|
||||
|
||||
import { getPluginScript } from '@/api/modules/api-test/common';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -898,13 +891,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
const statusText = computed(() => {
|
||||
if (props.detail.inconsistentWithApi) {
|
||||
return t('case.definitionInconsistent');
|
||||
}
|
||||
// TODO 这里的交互参数等待协调
|
||||
});
|
||||
|
||||
// 查看diff对比
|
||||
function showDiffDrawer() {
|
||||
emit('showDiff');
|
||||
|
|
|
@ -87,6 +87,8 @@
|
|||
:active-api-case-id="activeApiCaseId"
|
||||
:active-defined-id="activeDefinedId"
|
||||
@close="closeDifferent"
|
||||
@clear-this-change="clearThisChangeHandler"
|
||||
@sync="syncHandler"
|
||||
/>
|
||||
</div>
|
||||
<tab-case-dependency v-else-if="activeKey === 'reference'" :source-id="caseDetail.id" />
|
||||
|
@ -396,11 +398,20 @@
|
|||
}
|
||||
|
||||
const isPriorityLocalExec = computed(() => executeRef.value?.isPriorityLocalExec ?? false);
|
||||
const caseId = ref<string>(route.query.id as string);
|
||||
// 忽略本次变更
|
||||
async function clearThisChangeHandler() {
|
||||
getCaseDetailInfo(caseId.value);
|
||||
}
|
||||
|
||||
function syncHandler(id: string) {
|
||||
// TODO 这里需要调用同步合并后的详情接口回显详情
|
||||
createAndEditCaseDrawerRef.value?.open(id, caseDetail.value, false);
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initProtocolList();
|
||||
const caseId = route.query.id;
|
||||
getCaseDetailInfo(caseId as string);
|
||||
getCaseDetailInfo(caseId.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<MsTag
|
||||
v-if="props.inconsistentWithApi"
|
||||
class="cursor-pointer font-normal"
|
||||
:type="props.ignoreApiDiff ? 'default' : 'warning'"
|
||||
theme="light"
|
||||
:tooltip-disabled="true"
|
||||
max-width="160px"
|
||||
@click.stop="showDiffDrawer"
|
||||
>
|
||||
<template #icon>
|
||||
<div class="!text-[var(--color-text-4)]">
|
||||
<MsIcon v-if="props.ignoreApiDiff" type="icon-icon_warning_filled" size="16" />
|
||||
<MsIcon v-else class="!text-[var(--color-text-4)]" type="icon-icon_warning_colorful" size="16" />
|
||||
</div>
|
||||
</template>
|
||||
<span class="ml-[4px]"> {{ statusText }}</span>
|
||||
</MsTag>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
inconsistentWithApi?: boolean;
|
||||
ignoreApiDiff?: boolean;
|
||||
ignoreApiChange?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'showDiff'): void;
|
||||
}>();
|
||||
|
||||
function showDiffDrawer() {
|
||||
emit('showDiff');
|
||||
}
|
||||
|
||||
const statusText = computed(() => {
|
||||
// 忽略每次变更
|
||||
if (props.ignoreApiChange) {
|
||||
return t('case.eachHasBeenIgnored');
|
||||
}
|
||||
// 忽略本次变更
|
||||
if (props.ignoreApiDiff) {
|
||||
return t('case.haveIgnoredTheChange');
|
||||
}
|
||||
// 与接口定义不一致
|
||||
if (props.inconsistentWithApi) {
|
||||
return t('case.definitionInconsistent');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -73,6 +73,8 @@
|
|||
:active-api-case-id="activeApiCaseId"
|
||||
:active-defined-id="activeDefinedId"
|
||||
@close="closeDifferent"
|
||||
@clear-this-change="clearThisChangeHandler"
|
||||
@sync="syncHandler"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]">
|
||||
|
@ -132,9 +134,11 @@
|
|||
isDrawer?: boolean; // 抽屉
|
||||
detail: RequestParam;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'updateFollow'): void;
|
||||
(e: 'deleteCase', id: string): void;
|
||||
(e: 'loadCase', id: string): void;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
@ -342,6 +346,16 @@
|
|||
activeDefinedId.value = '';
|
||||
}
|
||||
|
||||
// 忽略本次变更
|
||||
async function clearThisChangeHandler() {
|
||||
emit('loadCase', props.detail.id as string);
|
||||
}
|
||||
|
||||
function syncHandler(id: string) {
|
||||
// TODO 这里需要调用同步合并后的详情接口回显详情
|
||||
createAndEditCaseDrawerRef.value?.open(id, caseDetail.value, false);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.detail,
|
||||
() => {
|
||||
|
|
|
@ -275,6 +275,7 @@
|
|||
</a-modal>
|
||||
<createAndEditCaseDrawer
|
||||
ref="createAndEditCaseDrawerRef"
|
||||
v-model:visible="showCaseVisible"
|
||||
:api-detail="apiDetail"
|
||||
@load-case="loadCaseListAndResetSelector()"
|
||||
@show-diff="showDifferences"
|
||||
|
@ -300,13 +301,21 @@
|
|||
<!-- 执行结果抽屉 -->
|
||||
<caseAndScenarioReportDrawer v-model:visible="showExecuteResult" :report-id="activeReportId" />
|
||||
<!-- 同步抽屉 -->
|
||||
<SyncModal v-model:visible="showSyncModal" :batch-params="batchParams" />
|
||||
<SyncModal
|
||||
ref="syncModalRef"
|
||||
v-model:visible="showSyncModal"
|
||||
:loading="syncLoading"
|
||||
:batch-params="batchParams"
|
||||
@batch-sync="handleBatchSync"
|
||||
/>
|
||||
<!-- diff对比抽屉 -->
|
||||
<DifferentDrawer
|
||||
v-model:visible="showDifferentDrawer"
|
||||
:active-api-case-id="activeApiCaseId"
|
||||
:active-defined-id="activeDefinedId"
|
||||
@close="closeDifferent"
|
||||
@clear-this-change="handleClearThisChange"
|
||||
@sync="syncHandler"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
@ -339,6 +348,7 @@
|
|||
batchDeleteCase,
|
||||
batchEditCase,
|
||||
batchExecuteCase,
|
||||
caseTableBatchSync,
|
||||
deleteCase,
|
||||
dragSort,
|
||||
getCaseDetail,
|
||||
|
@ -353,6 +363,7 @@
|
|||
import { characterLimit, operationWidth } from '@/utils';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import type { batchSyncForm } from '@/models/apiTest/management';
|
||||
import { ApiCaseDetail } from '@/models/apiTest/management';
|
||||
import { DragSortParams } from '@/models/common';
|
||||
import { RequestCaseStatus } from '@/enums/apiEnum';
|
||||
|
@ -860,7 +871,6 @@
|
|||
const batchConditionParams = ref<any>();
|
||||
|
||||
const showSyncModal = ref<boolean>(false);
|
||||
// 同步 TODO 等待联调
|
||||
function syncParams() {
|
||||
showSyncModal.value = true;
|
||||
}
|
||||
|
@ -975,7 +985,6 @@
|
|||
const activeDefinedId = ref<string>('');
|
||||
const showDifferentDrawer = ref<boolean>(false);
|
||||
|
||||
// 查看对比 TODO 等待联调
|
||||
async function showDifferences(record: ApiCaseDetail) {
|
||||
activeApiCaseId.value = record.id;
|
||||
activeDefinedId.value = record.apiDefinitionId;
|
||||
|
@ -987,6 +996,48 @@
|
|||
activeDefinedId.value = '';
|
||||
}
|
||||
|
||||
const syncLoading = ref<boolean>(false);
|
||||
const syncModalRef = ref<InstanceType<typeof SyncModal>>();
|
||||
// 批量同步
|
||||
async function handleBatchSync(syncForm: batchSyncForm) {
|
||||
try {
|
||||
syncLoading.value = true;
|
||||
const selectModules = await getModuleIds();
|
||||
const params = await genBatchConditionParams();
|
||||
await caseTableBatchSync({
|
||||
selectIds: batchParams.value?.selectedIds || [],
|
||||
selectAll: !!batchParams.value?.selectAll,
|
||||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
...params,
|
||||
...syncForm,
|
||||
moduleIds: selectModules,
|
||||
});
|
||||
Message.success(t('bugManagement.syncSuccess'));
|
||||
syncModalRef.value?.resetForm();
|
||||
resetSelector();
|
||||
loadCaseListAndResetSelector();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
syncLoading.value = false;
|
||||
}
|
||||
}
|
||||
const showCaseVisible = ref(false);
|
||||
// 清除本次变更
|
||||
async function handleClearThisChange() {
|
||||
await loadCaseList();
|
||||
await getCaseDetailInfo(activeApiCaseId.value);
|
||||
if (showCaseVisible.value) {
|
||||
createAndEditCaseDrawerRef.value?.open(caseDetail.value.apiDefinitionId, caseDetail.value as RequestParam, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 对比抽屉同步成功打开编辑
|
||||
function syncHandler(definedId: string) {
|
||||
// TODO 这里调用同步后的最新的合并后的详情,打开编辑抽屉用户手动保存更新即可生效
|
||||
createAndEditCaseDrawerRef.value?.open(definedId, caseDetail.value as RequestParam, false);
|
||||
}
|
||||
defineExpose({
|
||||
loadCaseList,
|
||||
});
|
||||
|
|
|
@ -71,20 +71,12 @@
|
|||
</a-form>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="px-[16px] font-medium">{{ t('apiTestManagement.requestParams') }}</div>
|
||||
<!-- 与定义不一致 TODO 等待联调 -->
|
||||
<MsTag
|
||||
v-if="detailForm.inconsistentWithApi"
|
||||
class="cursor-pointer"
|
||||
type="warning"
|
||||
theme="light"
|
||||
:tooltip-disabled="true"
|
||||
@click="showDiffDrawer"
|
||||
>
|
||||
<template #icon>
|
||||
<MsIcon type="icon-icon_warning_colorful" size="16" />
|
||||
</template>
|
||||
<span class="ml-[8px]"> {{ t('case.definitionInconsistent') }}</span>
|
||||
</MsTag>
|
||||
<ApiChangeTag
|
||||
:ignore-api-change="detailForm.ignoreApiChange"
|
||||
:ignore-api-diff="detailForm.ignoreApiDiff"
|
||||
:inconsistent-with-api="detailForm.inconsistentWithApi"
|
||||
@show-diff="showDiffDrawer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<requestComposition
|
||||
|
@ -111,7 +103,6 @@
|
|||
|
||||
import MsDetailCard, { type Description } from '@/components/pure/ms-detail-card/index.vue';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
|
||||
|
@ -120,6 +111,7 @@
|
|||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||
import executeButton from '@/views/api-test/components/executeButton.vue';
|
||||
import requestComposition, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||
import ApiChangeTag from '@/views/api-test/management/components/management/case/apiChangeTag.vue';
|
||||
|
||||
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
||||
import {
|
||||
|
@ -153,7 +145,10 @@
|
|||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const innerVisible = ref(false);
|
||||
const innerVisible = defineModel<boolean>('visible', {
|
||||
default: false,
|
||||
});
|
||||
|
||||
const drawerLoading = ref(false);
|
||||
|
||||
const apiDefinitionId = ref('');
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<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="form.checkType">
|
||||
<a-checkbox-group v-model="checkType">
|
||||
<a-checkbox v-for="item of checkList" :key="item.value" :value="item.value">
|
||||
<div class="flex items-center"
|
||||
>{{ item.label }}
|
||||
|
@ -31,17 +31,21 @@
|
|||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
<a-divider direction="vertical" :margin="0" class="!mr-[8px]"></a-divider>
|
||||
<a-switch v-model:model-value="form.ignoreUpdate" size="small" />
|
||||
<a-switch
|
||||
v-model:model-value="form.ignoreApiChange"
|
||||
:before-change="(val) => changeIgnore(val)"
|
||||
size="small"
|
||||
/>
|
||||
<div class="ml-[8px]">{{ t('case.ignoreAllChange') }}</div>
|
||||
<a-divider direction="vertical" :margin="8"></a-divider>
|
||||
<a-switch v-model:model-value="form.deleteParams" size="small" />
|
||||
<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-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">
|
||||
{{ t('case.ignoreAllChange') }}
|
||||
<a-button class="mr-[12px]" type="outline" @click="clearThisChangeHandler">
|
||||
{{ t('case.ignoreThisChange') }}
|
||||
</a-button>
|
||||
<a-button type="primary" :loading="syncLoading" :disabled="!form.checkType.length" @click="confirmBatchSync">
|
||||
<a-button type="primary" :loading="syncLoading" :disabled="!checkType.length" @click="confirmSync">
|
||||
{{ t('case.apiSyncChange') }}
|
||||
</a-button>
|
||||
</div>
|
||||
|
@ -87,6 +91,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
|
@ -94,10 +99,17 @@
|
|||
import DiffItem from './diffItem.vue';
|
||||
import DiffRequestBody from './diffRequestBody.vue';
|
||||
|
||||
import { getCaseDetail, getDefinitionDetail } from '@/api/modules/api-test/management';
|
||||
import {
|
||||
clearThisChange,
|
||||
diffDataRequest,
|
||||
getCaseDetail,
|
||||
getDefinitionDetail,
|
||||
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 { ApiDefinitionDetail } from '@/models/apiTest/management';
|
||||
import { RequestBodyFormat, RequestComposition } from '@/enums/apiEnum';
|
||||
|
||||
|
@ -113,6 +125,8 @@
|
|||
|
||||
const emit = defineEmits<{
|
||||
(e: 'close'): void;
|
||||
(e: 'clearThisChange'): void;
|
||||
(e: 'sync', activeDefinedId: string): void;
|
||||
}>();
|
||||
|
||||
const showDiffVisible = defineModel<boolean>('visible', {
|
||||
|
@ -140,18 +154,21 @@
|
|||
]);
|
||||
|
||||
const initForm = {
|
||||
deleteParams: false,
|
||||
checkType: [],
|
||||
deleteRedundantParam: false,
|
||||
syncItems: {
|
||||
header: false,
|
||||
body: false,
|
||||
query: false,
|
||||
rest: false,
|
||||
},
|
||||
noticeApiCaseCreator: true,
|
||||
noticeApiScenarioCreator: true,
|
||||
ignoreUpdate: false,
|
||||
ignoreUpdateType: ['THIS_TIME'],
|
||||
ignoreApiChange: false,
|
||||
};
|
||||
|
||||
const form = ref({ ...initForm });
|
||||
const checkType = ref([]);
|
||||
|
||||
// 忽略更新
|
||||
function changeIgnoreType() {}
|
||||
const form = ref({ ...initForm });
|
||||
|
||||
function cancel() {
|
||||
showDiffVisible.value = false;
|
||||
|
@ -159,7 +176,16 @@
|
|||
}
|
||||
const syncLoading = ref<boolean>(false);
|
||||
// 同步
|
||||
function confirmBatchSync() {}
|
||||
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 defaultCaseParams = inject<RequestParam>('defaultCaseParams');
|
||||
const caseDetail = ref<Record<string, any>>({});
|
||||
|
@ -297,12 +323,12 @@
|
|||
caseDetail.value = {
|
||||
...cloneDeep(defaultCaseParams as RequestParam),
|
||||
...({
|
||||
...res.request,
|
||||
...res,
|
||||
url: res.path,
|
||||
...parseRequestBodyResult,
|
||||
} as Partial<TabItem>),
|
||||
};
|
||||
form.value.ignoreApiChange = caseDetail.value.ignoreApiChange;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
@ -313,24 +339,68 @@
|
|||
try {
|
||||
const detail = await getDefinitionDetail(apiDefinitionId);
|
||||
apiDetailInfo.value = detail as ApiDefinitionDetail;
|
||||
apiDefinedRequest.value = detail.request as unknown as RequestParam;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function getDiffDataRequest(activeApiCaseId: string) {
|
||||
try {
|
||||
const result = await diffDataRequest(activeApiCaseId);
|
||||
const { caseRequest, apiRequest } = result;
|
||||
caseDetail.value = {
|
||||
...caseDetail.value,
|
||||
...caseRequest,
|
||||
num: caseDetail.value.num,
|
||||
};
|
||||
apiDefinedRequest.value = apiRequest;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
async function getRequestDetail(definedId: string, apiCaseId: string) {
|
||||
loading.value = true;
|
||||
try {
|
||||
await Promise.all([getApiDetail(definedId), getCaseDetailInfo(apiCaseId)]);
|
||||
await getDiffDataRequest(props.activeApiCaseId);
|
||||
processData();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
// 忽略并清除本次变更
|
||||
async function clearThisChangeHandler() {
|
||||
if (props.activeApiCaseId) {
|
||||
try {
|
||||
await clearThisChange(props.activeApiCaseId);
|
||||
getRequestDetail(props.activeDefinedId, props.activeApiCaseId);
|
||||
emit('clearThisChange');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 忽略每次变更
|
||||
async function changeIgnore(newValue: string | number | boolean) {
|
||||
try {
|
||||
await ignoreEveryTimeChange(props.activeApiCaseId, newValue as boolean);
|
||||
Message.success(newValue ? t('case.eachHasBeenIgnored') : t('case.eachHasBeenIgnoredClosed'));
|
||||
getRequestDetail(props.activeDefinedId, props.activeApiCaseId);
|
||||
emit('clearThisChange');
|
||||
return false;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.activeDefinedId && props.activeApiCaseId) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
title-align="start"
|
||||
class="ms-modal-upload ms-modal-medium"
|
||||
:width="600"
|
||||
@close="cancel"
|
||||
@close="resetForm"
|
||||
>
|
||||
<template #title>
|
||||
{{ t('case.apiSyncChange') }}
|
||||
|
@ -21,7 +21,7 @@
|
|||
<div class="mb-[8px]">
|
||||
{{ t('case.syncItem') }}
|
||||
</div>
|
||||
<a-checkbox-group v-model="form.checkType">
|
||||
<a-checkbox-group v-model="checkType">
|
||||
<a-checkbox v-for="item of checkList" :key="item.value" :value="item.value">
|
||||
<div class="flex items-center">
|
||||
{{ item.label }}
|
||||
|
@ -37,7 +37,7 @@
|
|||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
<div class="my-[16px] flex items-center">
|
||||
<a-switch v-model:model-value="form.deleteParams" size="small" />
|
||||
<a-switch v-model:model-value="form.deleteRedundantParam" size="small" />
|
||||
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('case.deleteNotCorrespondValue') }}</div>
|
||||
</div>
|
||||
|
||||
|
@ -53,17 +53,17 @@
|
|||
</a-tooltip>
|
||||
</div>
|
||||
<div class="my-[16px] flex items-center">
|
||||
<a-switch v-model:model-value="form.noticeApiCaseCreator" size="small" />
|
||||
<a-switch v-model:model-value="form.notificationConfig.apiCreator" size="small" />
|
||||
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('case.NoticeApiCaseCreator') }}</div>
|
||||
</div>
|
||||
<div class="my-[16px] flex items-center">
|
||||
<a-switch v-model:model-value="form.noticeApiScenarioCreator" size="small" />
|
||||
<a-switch v-model:model-value="form.notificationConfig.scenarioCreator" size="small" />
|
||||
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('case.NoticeApiScenarioCreator') }}</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<a-button type="secondary" @click="cancel">{{ t('common.cancel') }}</a-button>
|
||||
<a-button type="primary" :loading="syncLoading" :disabled="!form.checkType.length" @click="confirmBatchSync">
|
||||
<a-button type="secondary" @click="resetForm">{{ t('common.cancel') }}</a-button>
|
||||
<a-button type="primary" :loading="props.loading" :disabled="!checkType.length" @click="confirmBatchSync">
|
||||
{{ t('case.apiSyncChange') }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
@ -72,31 +72,47 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import type { BatchActionQueryParams } from '@/components/pure/ms-table/type';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import type { batchSyncForm, syncItem } from '@/models/apiTest/management';
|
||||
import { RequestComposition } from '@/enums/apiEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
batchParams: BatchActionQueryParams;
|
||||
loading: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'batchSync', form: batchSyncForm): void;
|
||||
}>();
|
||||
|
||||
const showBatchSyncModal = defineModel<boolean>('visible', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
const initForm = {
|
||||
deleteParams: false,
|
||||
checkType: [],
|
||||
noticeApiCaseCreator: true,
|
||||
noticeApiScenarioCreator: true,
|
||||
const initForm: batchSyncForm = {
|
||||
notificationConfig: {
|
||||
apiCreator: false,
|
||||
scenarioCreator: false,
|
||||
},
|
||||
// 同步项目
|
||||
syncItems: {
|
||||
header: false,
|
||||
body: false,
|
||||
query: false,
|
||||
rest: false,
|
||||
},
|
||||
deleteRedundantParam: false,
|
||||
};
|
||||
|
||||
const form = ref({ ...initForm });
|
||||
const form = ref<batchSyncForm>(cloneDeep(initForm));
|
||||
const checkType = ref([]);
|
||||
|
||||
const checkList = ref([
|
||||
{
|
||||
|
@ -118,13 +134,24 @@
|
|||
},
|
||||
]);
|
||||
|
||||
const syncLoading = ref<boolean>(false);
|
||||
|
||||
function cancel() {
|
||||
function resetForm() {
|
||||
form.value = cloneDeep(initForm);
|
||||
checkType.value = [];
|
||||
showBatchSyncModal.value = false;
|
||||
}
|
||||
// 同步 TODO 等待联调
|
||||
function confirmBatchSync() {}
|
||||
|
||||
function confirmBatchSync() {
|
||||
checkType.value.forEach((e: string) => {
|
||||
const key = e.toLowerCase() as keyof syncItem;
|
||||
form.value.syncItems[key] = true;
|
||||
});
|
||||
|
||||
emit('batchSync', form.value);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
resetForm,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -228,6 +228,7 @@ export default {
|
|||
'case.definitionInconsistent': 'Inconsistent with the definition',
|
||||
'case.haveIgnoredTheChange': 'Have ignored the change',
|
||||
'case.eachHasBeenIgnored': 'Each change difference has been ignored',
|
||||
'case.eachHasBeenIgnoredClosed': 'Closed Ignore each change difference',
|
||||
'case.apiCaseList': 'List',
|
||||
'case.apiCaseDetail': 'Use case details',
|
||||
'case.belongingApi': 'Belonging interface',
|
||||
|
|
|
@ -215,6 +215,7 @@ export default {
|
|||
'case.definitionInconsistent': '与定义不一致',
|
||||
'case.haveIgnoredTheChange': '已忽略本次变更差异',
|
||||
'case.eachHasBeenIgnored': '已忽略每次变更差异',
|
||||
'case.eachHasBeenIgnoredClosed': '已关闭忽略每次变更差异',
|
||||
'case.apiCaseList': '列表',
|
||||
'case.apiCaseDetail': '用例详情',
|
||||
'case.belongingApi': '所属接口',
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
</span>
|
||||
</template>
|
||||
<template #name="{ record }">
|
||||
<div class="one-line-text">{{ characterLimit(record.name) }}</div>
|
||||
<div class="one-line-text">{{ record.name }}</div>
|
||||
</template>
|
||||
<template #caseLevel="{ record }">
|
||||
<a-select
|
||||
|
|
|
@ -29,8 +29,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||
|
||||
|
@ -61,7 +59,7 @@
|
|||
(e: 'handleSummary', content: string): void;
|
||||
}>();
|
||||
|
||||
const innerSummary = useVModel(props, 'richText', emit);
|
||||
const innerSummary = ref(props.richText);
|
||||
|
||||
function handleCancel() {
|
||||
emit('cancel');
|
||||
|
@ -135,6 +133,18 @@
|
|||
emit('handleClick');
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.richText,
|
||||
(val) => {
|
||||
if (val) {
|
||||
innerSummary.value = { ...val };
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -173,7 +173,7 @@
|
|||
:is-preview="props.isPreview"
|
||||
/>
|
||||
<Summary
|
||||
v-else-if="item.value === ReportCardTypeEnum.SUMMARY"
|
||||
v-if="item.value === ReportCardTypeEnum.SUMMARY"
|
||||
:rich-text="getContent(item)"
|
||||
:share-id="shareId"
|
||||
:is-preview="props.isPreview"
|
||||
|
|
|
@ -89,9 +89,9 @@ export default {
|
|||
'testPlan.planForm.repeatCaseTip1': 'Enable: Repeatedly associate the same case',
|
||||
'testPlan.planForm.repeatCaseTip2': 'Close: Cannot be associated with the same case repeatedly',
|
||||
'testPlan.planForm.enableAutomaticStatusTip':
|
||||
'Enable: function cases associated interface/UI/case execution successful performance, function of the use case status automatically updated to success',
|
||||
'Enable: function case execution successful cases associated interface, function of the use case status automatically updated to success',
|
||||
'testPlan.planForm.closeAutomaticStatusTip':
|
||||
'Close: the function of use case execution result is not affected by the interface/UI/performance',
|
||||
'Close: The result of execution of a functional use case is not affected by the interface',
|
||||
'testPlan.planForm.passThresholdTip': 'If the pass rate reaches the specified pass threshold, the result is passed',
|
||||
'testPlan.planForm.pickCases': 'Select cases',
|
||||
'testPlan.testPlanDetail.executed': 'Executed',
|
||||
|
|
|
@ -85,9 +85,8 @@ export default {
|
|||
'testPlan.planForm.selectPlanGroup': '选择计划组',
|
||||
'testPlan.planForm.repeatCaseTip1': '开启:可重复关联同一个用例',
|
||||
'testPlan.planForm.repeatCaseTip2': '关闭:不可重复关联同一用例',
|
||||
'testPlan.planForm.enableAutomaticStatusTip':
|
||||
'开启:功能用例关联的接口/UI/性能用例执行成功,功能用例的状态自动更新为成功',
|
||||
'testPlan.planForm.closeAutomaticStatusTip': '关闭:功能用例的执行结果不受接口/UI/性能的影响',
|
||||
'testPlan.planForm.enableAutomaticStatusTip': '开启:功能用例关联的接口用例执行成功,功能用例的状态自动更新为成功',
|
||||
'testPlan.planForm.closeAutomaticStatusTip': '关闭:功能用例的执行结果不受接口的影响',
|
||||
'testPlan.planForm.passThresholdTip': '通过率达到设置的通过阈值时,报告结果为通过',
|
||||
'testPlan.planForm.pickCases': '选择用例',
|
||||
'testPlan.testPlanDetail.executed': '已执行',
|
||||
|
|
Loading…
Reference in New Issue