fix(全局): bug修复

This commit is contained in:
baiqi 2024-04-22 21:01:15 +08:00 committed by Craftsman
parent 5b60c332db
commit 1a13f39a96
21 changed files with 319 additions and 233 deletions

View File

@ -124,7 +124,7 @@
:selectable="true" :selectable="true"
:columns="xPathColumns" :columns="xPathColumns"
:scroll="{ minWidth: '700px' }" :scroll="{ minWidth: '700px' }"
:default-param-item="xPathDefaultParamItem" :default-param-item="defaultAssertParamsItem"
@change="(data:any[],isInit?: boolean) => handleChange(data, ResponseBodyAssertionType.XPATH,isInit)" @change="(data:any[],isInit?: boolean) => handleChange(data, ResponseBodyAssertionType.XPATH,isInit)"
@more-action-select="(e,r)=> handleExtractParamMoreActionSelect(e,r as ExpressionConfig)" @more-action-select="(e,r)=> handleExtractParamMoreActionSelect(e,r as ExpressionConfig)"
> >
@ -363,7 +363,7 @@
import { TableColumnData, TableData } from '@arco-design/web-vue'; import { TableColumnData, TableData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { EQUAL, statusCodeOptions } from '@/components/pure/ms-advance-filter'; import { statusCodeOptions } from '@/components/pure/ms-advance-filter';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types'; import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import { TableOperationColumn } from '@/components/business/ms-user-group-comp/authTable.vue'; import { TableOperationColumn } from '@/components/business/ms-user-group-comp/authTable.vue';
import fastExtraction from '@/views/api-test/components/fastExtraction/index.vue'; import fastExtraction from '@/views/api-test/components/fastExtraction/index.vue';
@ -387,15 +387,14 @@
RegexExtract, RegexExtract,
XPathExtract, XPathExtract,
} from '@/models/apiTest/common'; } from '@/models/apiTest/common';
import { RequestExtractExpressionEnum, ResponseBodyAssertionType } from '@/enums/apiEnum';
import { import {
RequestExtractEnvType, defaultAssertParamsItem,
RequestExtractExpressionEnum, defaultExtractParamItem,
RequestExtractExpressionRuleType, jsonPathDefaultParamItem,
RequestExtractResultMatchingRule, regexDefaultParamItem,
RequestExtractScope, } from '@/views/api-test/components/config';
ResponseBodyAssertionType,
ResponseBodyXPathAssertionFormat,
} from '@/enums/apiEnum';
const { t } = useI18n(); const { t } = useI18n();
@ -445,20 +444,6 @@
// const disabledExpressionSuffix = ref(false); // const disabledExpressionSuffix = ref(false);
export type ExpressionConfig = (RegexExtract | JSONPathExtract | XPathExtract) & Record<string, any>; export type ExpressionConfig = (RegexExtract | JSONPathExtract | XPathExtract) & Record<string, any>;
const defaultExtractParamItem: ExpressionConfig = {
enable: true,
variableName: '',
variableType: RequestExtractEnvType.TEMPORARY,
extractScope: RequestExtractScope.BODY,
expression: '',
extractType: RequestExtractExpressionEnum.JSON_PATH,
expressionMatchingRule: RequestExtractExpressionRuleType.EXPRESSION,
resultMatchingRule: RequestExtractResultMatchingRule.RANDOM,
resultMatchingRuleNum: 1,
responseFormat: ResponseBodyXPathAssertionFormat.XML,
moreSettingPopoverVisible: false,
};
const activeRecord = ref({ ...defaultExtractParamItem }); // const activeRecord = ref({ ...defaultExtractParamItem }); //
const responseRadios = [ const responseRadios = [
@ -506,50 +491,6 @@
}, },
]; ];
// json
const jsonPathDefaultParamItem = {
enable: true,
variableName: '',
variableType: RequestExtractEnvType.TEMPORARY,
extractScope: RequestExtractScope.BODY,
expression: '',
condition: EQUAL.value,
extractType: RequestExtractExpressionEnum.JSON_PATH,
expressionMatchingRule: RequestExtractExpressionRuleType.EXPRESSION,
resultMatchingRule: RequestExtractResultMatchingRule.RANDOM,
resultMatchingRuleNum: 1,
responseFormat: ResponseBodyXPathAssertionFormat.XML,
moreSettingPopoverVisible: false,
};
// xpath
const xPathDefaultParamItem = {
expression: '',
enable: true,
valid: true,
variableType: RequestExtractEnvType.TEMPORARY,
extractScope: RequestExtractScope.BODY,
extractType: RequestExtractExpressionEnum.X_PATH,
expressionMatchingRule: RequestExtractExpressionRuleType.EXPRESSION,
resultMatchingRule: RequestExtractResultMatchingRule.RANDOM,
resultMatchingRuleNum: 1,
responseFormat: ResponseBodyXPathAssertionFormat.XML,
moreSettingPopoverVisible: false,
};
// xpath
const regexDefaultParamItem = {
expression: '',
enable: true,
valid: true,
variableType: RequestExtractEnvType.TEMPORARY,
extractScope: RequestExtractScope.BODY,
extractType: RequestExtractExpressionEnum.REGEX,
expressionMatchingRule: RequestExtractExpressionRuleType.EXPRESSION,
resultMatchingRule: RequestExtractResultMatchingRule.RANDOM,
resultMatchingRuleNum: 1,
responseFormat: ResponseBodyXPathAssertionFormat.XML,
moreSettingPopoverVisible: false,
};
const handleChange = (data: any[], type: string, isInit?: boolean) => { const handleChange = (data: any[], type: string, isInit?: boolean) => {
switch (type) { switch (type) {
case ResponseBodyAssertionType.JSON_PATH: case ResponseBodyAssertionType.JSON_PATH:

View File

@ -9,9 +9,9 @@
</div> </div>
</a-button> </a-button>
<template #content> <template #content>
<a-doption v-for="item in assertOptionSource" :key="item.value" :value="item.value">{{ <a-doption v-for="item in assertOptionSource" :key="item.value" :value="item.value">
item.label {{ item.label }}
}}</a-doption> </a-doption>
</template> </template>
</a-dropdown> </a-dropdown>
<div v-if="props.isDefinition && innerConfig" class="flex items-center"> <div v-if="props.isDefinition && innerConfig" class="flex items-center">

View File

@ -115,15 +115,26 @@
/** /**
* 替换文档 * 替换文档
* @param beautifyDoc 格式化后的文档 * @param beautifyDoc 格式化后的文档
* @param isHtml 是否是html
*/ */
function replaceDoc(beautifyDoc: string) { function replaceDoc(beautifyDoc: string, isHtml = false) {
// HTML icon // XML/HTML icon
flattenedXml.value = beautifyDoc let resultArr: XpathNode[] = [];
const tempStr = beautifyDoc
.replace(/</g, '&lt;') .replace(/</g, '&lt;')
.replace(/>/g, '&gt;') .replace(/>/g, '&gt;')
.replace(/(&lt;([^/][^&]*?)&gt;)/g, '<span style="color: rgb(var(--primary-5));cursor: pointer">$1📋</span>') .replace(/(&lt;([^/][^&]*?)&gt;)/g, '<span style="color: rgb(var(--primary-5));cursor: pointer">$1📋</span>');
if (isHtml) {
resultArr = HtmlBeautify(
tempStr.replace(/(\S)(?=<)/gs, '$1\n'), // html
{ ocd: true }
)
.split(/\r?\n/) .split(/\r?\n/)
.map((e) => ({ content: e, xpath: '' })); .map((e) => ({ content: e, xpath: '' }));
} else {
resultArr = tempStr.split(/\r?\n/).map((e) => ({ content: e, xpath: '' }));
}
flattenedXml.value = resultArr;
} }
/** /**
@ -142,8 +153,9 @@
isValidXml.value = true; isValidXml.value = true;
parsedXml.value = xmlDoc; parsedXml.value = xmlDoc;
const beautifyDoc = HtmlBeautify(props.xmlString, { ocd: true }); const beautifyDoc = HtmlBeautify(props.xmlString, { ocd: true });
replaceDoc(beautifyDoc); replaceDoc(beautifyDoc, true);
// HTML xpath // HTML xpath
tempXmls.value = [];
flattenHtml(xmlDoc.documentElement, ''); flattenHtml(xmlDoc.documentElement, '');
// XML/HTML xpath xpath // XML/HTML xpath xpath
flattenedXml.value = flattenedXml.value flattenedXml.value = flattenedXml.value
@ -157,7 +169,7 @@
xpath, xpath,
}; };
} }
return false; return e.content.includes('&lt;/') ? e : false;
}) })
.filter(Boolean) as any[]; .filter(Boolean) as any[];
emit('init', 'html'); emit('init', 'html');
@ -185,9 +197,11 @@
const beautifyDoc = new XmlBeautify().beautify(props.xmlString); const beautifyDoc = new XmlBeautify().beautify(props.xmlString);
replaceDoc(beautifyDoc); replaceDoc(beautifyDoc);
// XML xpath // XML xpath
tempXmls.value = [];
flattenXml(xmlDoc.documentElement, ''); flattenXml(xmlDoc.documentElement, '');
// XML xpath xpath // XML xpath xpath
flattenedXml.value = flattenedXml.value.map((e) => { flattenedXml.value = flattenedXml.value
.map((e) => {
const targetNodeIndex = tempXmls.value.findIndex((txt) => e.content.includes(`&lt;${txt.content}`)); const targetNodeIndex = tempXmls.value.findIndex((txt) => e.content.includes(`&lt;${txt.content}`));
if (targetNodeIndex >= 0) { if (targetNodeIndex >= 0) {
const { xpath } = tempXmls.value[targetNodeIndex]; const { xpath } = tempXmls.value[targetNodeIndex];
@ -197,8 +211,9 @@
xpath, xpath,
}; };
} }
return e; return false;
}); })
.filter(Boolean) as any[];
emit('init', 'xml'); emit('init', 'xml');
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console

View File

@ -78,6 +78,8 @@ export interface ApiScenarioTableItem {
deleteUser: string; deleteUser: string;
deleteTime: number; deleteTime: number;
deleted: boolean; deleted: boolean;
environmentId: string;
environmentName: string;
createUserName: string; createUserName: string;
updateUserName: string; updateUserName: string;
deleteUserName: string; deleteUserName: string;

View File

@ -276,7 +276,7 @@
</div> </div>
<template v-if="condition.dataSourceId"> <template v-if="condition.dataSourceId">
<div class="mb-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.sqlScript') }}</div> <div class="mb-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.sqlScript') }}</div>
<div class="mb-[8px] h-[300px]"> <div class="mb-[8px]">
<MsCodeEditor <MsCodeEditor
v-model:model-value="condition.script" v-model:model-value="condition.script"
:read-only="props.disabled" :read-only="props.disabled"

View File

@ -1,5 +1,6 @@
<template> <template>
<div class="mb-[8px] flex items-center justify-between"> <div class="mb-[8px] flex items-center justify-between">
<div class="flex items-center gap-[8px]">
<a-dropdown @select="(val) => addCondition(val as ConditionType)"> <a-dropdown @select="(val) => addCondition(val as ConditionType)">
<a-button type="outline" :disabled="props.disabled"> <a-button type="outline" :disabled="props.disabled">
<template #icon> <template #icon>
@ -13,6 +14,10 @@
</a-doption> </a-doption>
</template> </template>
</a-dropdown> </a-dropdown>
<div v-if="$slots.dropdownAppend" class="flex items-center">
<slot name="dropdownAppend"></slot>
</div>
</div>
<div v-if="$slots.titleRight" class="flex items-center"> <div v-if="$slots.titleRight" class="flex items-center">
<slot name="titleRight"></slot> <slot name="titleRight"></slot>
</div> </div>

View File

@ -1,3 +1,5 @@
import { EQUAL } from '@/components/pure/ms-advance-filter';
import { import {
EnableKeyValueParam, EnableKeyValueParam,
ExecuteBody, ExecuteBody,
@ -13,11 +15,19 @@ import {
RequestBodyFormat, RequestBodyFormat,
RequestCaseStatus, RequestCaseStatus,
RequestContentTypeEnum, RequestContentTypeEnum,
RequestExtractEnvType,
RequestExtractExpressionEnum,
RequestExtractExpressionRuleType,
RequestExtractResultMatchingRule,
RequestExtractScope,
RequestParamsType, RequestParamsType,
ResponseBodyFormat, ResponseBodyFormat,
ResponseBodyXPathAssertionFormat,
ResponseComposition, ResponseComposition,
} from '@/enums/apiEnum'; } from '@/enums/apiEnum';
import type { ExpressionConfig } from './fastExtraction/moreSetting.vue';
// 请求 body 参数表格默认行的值 // 请求 body 参数表格默认行的值
export const defaultBodyParamsItem: ExecuteRequestFormBodyFormValue = { export const defaultBodyParamsItem: ExecuteRequestFormBodyFormValue = {
key: '', key: '',
@ -173,3 +183,47 @@ export const defaultAssertXpathParamsItem: ResponseAssertionItem = {
expression: '', expression: '',
enable: true, enable: true,
}; };
// 断言 xpath
export const defaultExtractParamItem: ExpressionConfig = {
enable: true,
variableName: '',
variableType: RequestExtractEnvType.TEMPORARY,
extractScope: RequestExtractScope.BODY,
expression: '',
extractType: RequestExtractExpressionEnum.JSON_PATH,
expressionMatchingRule: RequestExtractExpressionRuleType.EXPRESSION,
resultMatchingRule: RequestExtractResultMatchingRule.RANDOM,
resultMatchingRuleNum: 1,
responseFormat: ResponseBodyXPathAssertionFormat.XML,
moreSettingPopoverVisible: false,
};
// 断言 json默认值
export const jsonPathDefaultParamItem = {
enable: true,
variableName: '',
variableType: RequestExtractEnvType.TEMPORARY,
extractScope: RequestExtractScope.BODY,
expression: '',
condition: EQUAL.value,
extractType: RequestExtractExpressionEnum.JSON_PATH,
expressionMatchingRule: RequestExtractExpressionRuleType.EXPRESSION,
resultMatchingRule: RequestExtractResultMatchingRule.RANDOM,
resultMatchingRuleNum: 1,
responseFormat: ResponseBodyXPathAssertionFormat.XML,
moreSettingPopoverVisible: false,
};
// 断言 正则默认值
export const regexDefaultParamItem = {
expression: '',
enable: true,
valid: true,
variableType: RequestExtractEnvType.TEMPORARY,
extractScope: RequestExtractScope.BODY,
extractType: RequestExtractExpressionEnum.REGEX,
expressionMatchingRule: RequestExtractExpressionRuleType.EXPRESSION,
resultMatchingRule: RequestExtractResultMatchingRule.RANDOM,
resultMatchingRuleNum: 1,
responseFormat: ResponseBodyXPathAssertionFormat.XML,
moreSettingPopoverVisible: false,
};

View File

@ -237,10 +237,7 @@
matchResult.value = [nodes]; matchResult.value = [nodes];
} else if (Array.isArray(nodes)) { } else if (Array.isArray(nodes)) {
// //
matchResult.value = nodes matchResult.value = nodes.map((node) => node.firstChild?.nodeValue || false).filter(Boolean);
.map((node) => node.textContent?.split('\n') || false)
.flat(Infinity)
.filter(Boolean);
} else { } else {
// //
matchResult.value = nodes.textContent ? [nodes.textContent] : []; matchResult.value = nodes.textContent ? [nodes.textContent] : [];

View File

@ -87,11 +87,12 @@
v-if="columnConfig.inputType === 'autoComplete'" v-if="columnConfig.inputType === 'autoComplete'"
v-model:model-value="record[columnConfig.dataIndex as string]" v-model:model-value="record[columnConfig.dataIndex as string]"
:disabled="props.disabledExceptParam || columnConfig.disabledColumn" :disabled="props.disabledExceptParam || columnConfig.disabledColumn"
:data="record[columnConfig.dataIndex as string] !== '' ? columnConfig.autoCompleteParams?.filter((e) => e.isShow === true) : columnConfig.autoCompleteParams" :data="getAutoCompleteData(columnConfig, record)"
class="ms-form-table-input" class="ms-form-table-input"
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }" :trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
:filter-option="false" :filter-option="false"
size="mini" size="mini"
@focus="handleAutoCompleteFocus(record)"
@search="(val) => handleSearchParams(val, columnConfig)" @search="(val) => handleSearchParams(val, columnConfig)"
@change="() => addTableLine(rowIndex, columnConfig.addLineDisabled)" @change="() => addTableLine(rowIndex, columnConfig.addLineDisabled)"
@select="(val) => selectAutoComplete(val, record, columnConfig)" @select="(val) => selectAutoComplete(val, record, columnConfig)"
@ -1041,6 +1042,21 @@
}); });
} }
const activeRecord = ref<Record<string, any>>({});
function getAutoCompleteData(columnConfig: ParamTableColumn, record: Record<string, any>) {
if (activeRecord.value.id !== record.id) {
//
return [];
}
return activeRecord.value[columnConfig.dataIndex as string] !== ''
? columnConfig.autoCompleteParams?.filter((e) => e.isShow === true)
: columnConfig.autoCompleteParams;
}
function handleAutoCompleteFocus(record: Record<string, any>) {
activeRecord.value = record;
}
function selectAutoComplete(val: string, record: Record<string, any>, item: FormTableColumn) { function selectAutoComplete(val: string, record: Record<string, any>, item: FormTableColumn) {
record[item.dataIndex as string] = val; record[item.dataIndex as string] = val;
} }

View File

@ -126,7 +126,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { Message, SelectOptionData } from '@arco-design/web-vue'; import { Message, SelectOptionData } from '@arco-design/web-vue';
import { cloneDeep, debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue'; import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
import MsTab from '@/components/pure/ms-tab/index.vue'; import MsTab from '@/components/pure/ms-tab/index.vue';
@ -142,17 +142,21 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import { ExecuteConditionConfig, PluginConfig } from '@/models/apiTest/common'; import { PluginConfig } from '@/models/apiTest/common';
import { ModuleTreeNode, TransferFileParams } from '@/models/common'; import { ModuleTreeNode, TransferFileParams } from '@/models/common';
import { RequestAuthType, RequestBodyFormat, RequestComposition, RequestConditionProcessor } from '@/enums/apiEnum'; import { RequestAuthType, RequestBodyFormat, RequestComposition } from '@/enums/apiEnum';
import { import {
defaultBodyParamsItem, defaultBodyParamsItem,
defaultHeaderParamsItem, defaultHeaderParamsItem,
defaultKeyValueParamItem,
defaultRequestParamsItem, defaultRequestParamsItem,
} from '@/views/api-test/components/config'; } from '@/views/api-test/components/config';
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils'; import {
filterAssertions,
filterConditionsSqlValidParams,
filterKeyValParams,
parseRequestBodyFiles,
} from '@/views/api-test/components/utils';
import type { Api } from '@form-create/arco-design'; import type { Api } from '@form-create/arco-design';
// Http // Http
@ -430,20 +434,6 @@
} }
} }
function filterConditionsSqlValidParams(condition: ExecuteConditionConfig) {
const conditionCopy = cloneDeep(condition);
conditionCopy.processors = conditionCopy.processors.map((processor) => {
if (processor.processorType === RequestConditionProcessor.SQL) {
processor.extractParams = filterKeyValParams(
processor.extractParams || [],
defaultKeyValueParamItem
).validParams;
}
return processor;
});
return conditionCopy;
}
// //
function makeRequestParams(executeType?: 'localExec' | 'serverExec') { function makeRequestParams(executeType?: 'localExec' | 'serverExec') {
const isExecute = executeType === 'localExec' || executeType === 'serverExec'; const isExecute = executeType === 'localExec' || executeType === 'serverExec';
@ -498,6 +488,7 @@
const requestName = requestVModel.value.name ?? ''; const requestName = requestVModel.value.name ?? '';
const requestModuleId = requestVModel.value.moduleId ?? ''; const requestModuleId = requestVModel.value.moduleId ?? '';
const { assertionConfig } = requestVModel.value.children[0];
return { return {
id: requestVModel.value.id.toString(), id: requestVModel.value.id.toString(),
name: requestName, name: requestName,
@ -511,7 +502,10 @@
children: [ children: [
{ {
polymorphicName: 'MsCommonElement', // MsCommonElement polymorphicName: 'MsCommonElement', // MsCommonElement
assertionConfig: requestVModel.value.children[0].assertionConfig, assertionConfig: {
...requestVModel.value.children[0].assertionConfig,
assertions: filterAssertions(assertionConfig, isExecute),
},
postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig), postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig),
preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig), preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig),
}, },

View File

@ -509,15 +509,13 @@
import { import {
casePriorityOptions, casePriorityOptions,
caseStatusOptions, caseStatusOptions,
defaultAssertParamsItem,
defaultAssertXpathParamsItem,
defaultBodyParamsItem, defaultBodyParamsItem,
defaultHeaderParamsItem, defaultHeaderParamsItem,
defaultKeyValueParamItem, defaultKeyValueParamItem,
defaultRequestParamsItem, defaultRequestParamsItem,
defaultResponse, defaultResponse,
} from '@/views/api-test/components/config'; } from '@/views/api-test/components/config';
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils'; import { filterAssertions, filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils';
import type { Api } from '@form-create/arco-design'; import type { Api } from '@form-create/arco-design';
// Http // Http
@ -1138,45 +1136,6 @@
// //
const { assertionConfig } = requestVModel.value.children[0]; const { assertionConfig } = requestVModel.value.children[0];
const assertionList = assertionConfig.assertions.map((assertItem: any) => {
const bodyAssertionDataByTypeList = filterKeyValParams(
assertItem?.bodyAssertionDataByType?.assertions || [],
defaultAssertParamsItem,
isExecute
).validParams;
return {
...assertItem,
bodyAssertionDataByType: {
...assertItem.bodyAssertionDataByType,
assertions: bodyAssertionDataByTypeList,
},
regexAssertion: {
...assertItem?.regexAssertion,
assertions: filterKeyValParams(
assertItem?.regexAssertion?.assertions || [],
defaultAssertXpathParamsItem,
isExecute
).validParams,
},
xpathAssertion: {
...assertItem.xpathAssertion,
assertions: filterKeyValParams(
assertItem?.xpathAssertion?.assertions || [],
defaultAssertXpathParamsItem,
isExecute
).validParams,
},
jsonPathAssertion: {
...assertItem.jsonPathAssertion,
assertions: filterKeyValParams(
assertItem?.jsonPathAssertion?.assertions || [],
defaultAssertParamsItem,
isExecute
).validParams,
},
};
});
return { return {
id: requestVModel.value.id.toString(), id: requestVModel.value.id.toString(),
reportId: reportId.value, reportId: reportId.value,
@ -1195,7 +1154,7 @@
polymorphicName: 'MsCommonElement', // MsCommonElement polymorphicName: 'MsCommonElement', // MsCommonElement
assertionConfig: { assertionConfig: {
...assertionConfig, ...assertionConfig,
assertions: assertionList, assertions: filterAssertions(assertionConfig, isExecute),
}, },
postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig), postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig),
preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig), preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig),

View File

@ -10,6 +10,9 @@
:sql-code-editor-height="props.sqlCodeEditorHeight" :sql-code-editor-height="props.sqlCodeEditorHeight"
@change="emit('change')" @change="emit('change')"
> >
<template v-if="$slots.dropdownAppend" #dropdownAppend>
<slot name="dropdownAppend" />
</template>
<template v-if="props.isDefinition" #titleRight> <template v-if="props.isDefinition" #titleRight>
<a-switch <a-switch
v-model:model-value="innerConfig.enableGlobal" v-model:model-value="innerConfig.enableGlobal"

View File

@ -8,6 +8,9 @@
add-text="apiTestDebug.precondition" add-text="apiTestDebug.precondition"
@change="emit('change')" @change="emit('change')"
> >
<template v-if="$slots.dropdownAppend" #dropdownAppend>
<slot name="dropdownAppend" />
</template>
<template v-if="props.isDefinition" #titleRight> <template v-if="props.isDefinition" #titleRight>
<a-switch <a-switch
v-model:model-value="innerConfig.enableGlobal" v-model:model-value="innerConfig.enableGlobal"

View File

@ -1,13 +1,17 @@
import { cloneDeep, isEqual } from 'lodash-es'; import { cloneDeep, isEqual } from 'lodash-es';
import { ExecuteBody } from '@/models/apiTest/common'; import { type ExecuteAssertionConfig, ExecuteBody, type ExecuteConditionConfig } from '@/models/apiTest/common';
import { RequestParamsType } from '@/enums/apiEnum'; import { RequestConditionProcessor, RequestParamsType, ResponseBodyAssertionType } from '@/enums/apiEnum';
import { import {
defaultAssertParamsItem,
defaultBodyParamsItem, defaultBodyParamsItem,
defaultExtractParamItem,
defaultHeaderParamsItem, defaultHeaderParamsItem,
defaultKeyValueParamItem, defaultKeyValueParamItem,
defaultRequestParamsItem, defaultRequestParamsItem,
jsonPathDefaultParamItem,
regexDefaultParamItem,
} from './config'; } from './config';
import type { RequestParam } from './requestComposition/index.vue'; import type { RequestParam } from './requestComposition/index.vue';
@ -129,13 +133,18 @@ export function filterKeyValParams<T>(
validParams: params, validParams: params,
}; };
} }
// id和enable属性不参与比较 // id、enable、valid属性不参与比较
delete lastData.id; delete lastData.id;
delete lastData.enable; delete lastData.enable;
delete lastData.valid;
delete defaultParam.valid;
delete defaultParam.id; delete defaultParam.id;
delete defaultParam.enable; delete defaultParam.enable;
const lastDataIsDefault = isEqual(lastData, defaultParam) || lastData.key === ''; const lastDataIsDefault = isEqual(lastData, defaultParam) || lastData.key === '';
let validParams: (T & Record<string, any>)[]; let validParams: (T & Record<string, any>)[];
if (defaultParam.condition) {
console.log(`assertionConfig`, lastDataIsDefault, lastData, defaultParam);
}
if (lastDataIsDefault) { if (lastDataIsDefault) {
// 如果最后一条数据是默认数据,非用户添加更改的,说明是无效参数,删除最后一个 // 如果最后一条数据是默认数据,非用户添加更改的,说明是无效参数,删除最后一个
validParams = params.slice(0, params.length - 1); validParams = params.slice(0, params.length - 1);
@ -170,3 +179,57 @@ export function getValidRequestTableParams(requestVModel: RequestParam) {
})) || [], })) || [],
}; };
} }
/**
*
* @param condition
*/
export function filterConditionsSqlValidParams(condition: ExecuteConditionConfig) {
const conditionCopy = cloneDeep(condition);
conditionCopy.processors = conditionCopy.processors.map((processor) => {
if (processor.processorType === RequestConditionProcessor.SQL) {
processor.extractParams = filterKeyValParams(processor.extractParams || [], defaultKeyValueParamItem).validParams;
}
return processor;
});
return conditionCopy;
}
/**
*
* @param assertionConfig
* @param isExecute
*/
export function filterAssertions(assertionConfig: ExecuteAssertionConfig, isExecute = false) {
return assertionConfig.assertions.map((assertItem: any) => {
return {
...assertItem,
bodyAssertionDataByType: {
...assertItem.bodyAssertionDataByType,
assertions: filterKeyValParams(
assertItem?.bodyAssertionDataByType?.assertions || [],
defaultAssertParamsItem,
isExecute
).validParams,
},
regexAssertion: {
...assertItem?.regexAssertion,
assertions: filterKeyValParams(assertItem?.regexAssertion?.assertions || [], regexDefaultParamItem, isExecute)
.validParams,
},
xpathAssertion: {
...assertItem.xpathAssertion,
assertions: filterKeyValParams(assertItem?.xpathAssertion?.assertions || [], defaultExtractParamItem, isExecute)
.validParams,
},
jsonPathAssertion: {
...assertItem.jsonPathAssertion,
assertions: filterKeyValParams(
assertItem?.jsonPathAssertion?.assertions || [],
jsonPathDefaultParamItem,
isExecute
).validParams,
},
};
});
}

View File

@ -395,7 +395,6 @@
EnableKeyValueParam, EnableKeyValueParam,
ExecuteApiRequestFullParams, ExecuteApiRequestFullParams,
ExecuteBody, ExecuteBody,
ExecuteConditionConfig,
ExecutePluginRequestParams, ExecutePluginRequestParams,
ExecuteRequestCommonParam, ExecuteRequestCommonParam,
ExecuteRequestFormBody, ExecuteRequestFormBody,
@ -409,7 +408,6 @@
RequestAuthType, RequestAuthType,
RequestBodyFormat, RequestBodyFormat,
RequestComposition, RequestComposition,
RequestConditionProcessor,
RequestMethods, RequestMethods,
ResponseComposition, ResponseComposition,
ScenarioStepType, ScenarioStepType,
@ -420,11 +418,15 @@
defaultBodyParams, defaultBodyParams,
defaultBodyParamsItem, defaultBodyParamsItem,
defaultHeaderParamsItem, defaultHeaderParamsItem,
defaultKeyValueParamItem,
defaultRequestParamsItem, defaultRequestParamsItem,
defaultResponse, defaultResponse,
} from '@/views/api-test/components/config'; } from '@/views/api-test/components/config';
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils'; import {
filterAssertions,
filterConditionsSqlValidParams,
filterKeyValParams,
parseRequestBodyFiles,
} from '@/views/api-test/components/utils';
import type { Api } from '@form-create/arco-design'; import type { Api } from '@form-create/arco-design';
// Http // Http
@ -995,20 +997,6 @@
verticalSplitBoxRef.value?.expand(0.6); verticalSplitBoxRef.value?.expand(0.6);
} }
function filterConditionsSqlValidParams(condition: ExecuteConditionConfig) {
const conditionCopy = cloneDeep(condition);
conditionCopy.processors = conditionCopy.processors.map((processor) => {
if (processor.processorType === RequestConditionProcessor.SQL) {
processor.extractParams = filterKeyValParams(
processor.extractParams || [],
defaultKeyValueParamItem
).validParams;
}
return processor;
});
return conditionCopy;
}
/** /**
* 生成请求参数 * 生成请求参数
* @param executeType 执行类型执行时传入 * @param executeType 执行类型执行时传入
@ -1062,6 +1050,7 @@
polymorphicName, polymorphicName,
}; };
} }
const { assertionConfig } = requestVModel.value.children[0];
return { return {
...requestParams, ...requestParams,
resourceId: requestVModel.value.resourceId, resourceId: requestVModel.value.resourceId,
@ -1077,7 +1066,10 @@
children: [ children: [
{ {
polymorphicName: 'MsCommonElement', // MsCommonElement polymorphicName: 'MsCommonElement', // MsCommonElement
assertionConfig: requestVModel.value.children[0].assertionConfig, assertionConfig: {
...requestVModel.value.children[0].assertionConfig,
assertions: filterAssertions(assertionConfig, isExecute),
},
postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig), postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig),
preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig), preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig),
}, },

View File

@ -304,13 +304,12 @@
import { characterLimit } from '@/utils'; import { characterLimit } from '@/utils';
import { scrollIntoView } from '@/utils/dom'; import { scrollIntoView } from '@/utils/dom';
import { ExecuteConditionConfig, PluginConfig, RequestResult } from '@/models/apiTest/common'; import { PluginConfig, RequestResult } from '@/models/apiTest/common';
import { ScenarioStepFileParams, ScenarioStepItem } from '@/models/apiTest/scenario'; import { ScenarioStepFileParams, ScenarioStepItem } from '@/models/apiTest/scenario';
import { import {
RequestAuthType, RequestAuthType,
RequestBodyFormat, RequestBodyFormat,
RequestComposition, RequestComposition,
RequestConditionProcessor,
RequestMethods, RequestMethods,
ResponseComposition, ResponseComposition,
ScenarioStepRefType, ScenarioStepRefType,
@ -322,11 +321,15 @@
defaultBodyParams, defaultBodyParams,
defaultBodyParamsItem, defaultBodyParamsItem,
defaultHeaderParamsItem, defaultHeaderParamsItem,
defaultKeyValueParamItem,
defaultRequestParamsItem, defaultRequestParamsItem,
defaultResponse, defaultResponse,
} from '@/views/api-test/components/config'; } from '@/views/api-test/components/config';
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils'; import {
filterAssertions,
filterConditionsSqlValidParams,
filterKeyValParams,
parseRequestBodyFiles,
} from '@/views/api-test/components/utils';
import { Api } from '@form-create/arco-design'; import { Api } from '@form-create/arco-design';
// Http // Http
const httpHeader = defineAsyncComponent(() => import('@/views/api-test/components/requestComposition/header.vue')); const httpHeader = defineAsyncComponent(() => import('@/views/api-test/components/requestComposition/header.vue'));
@ -824,20 +827,6 @@
verticalSplitBoxRef.value?.expand(0.6); verticalSplitBoxRef.value?.expand(0.6);
} }
function filterConditionsSqlValidParams(condition: ExecuteConditionConfig) {
const conditionCopy = cloneDeep(condition);
conditionCopy.processors = conditionCopy.processors.map((processor) => {
if (processor.processorType === RequestConditionProcessor.SQL) {
processor.extractParams = filterKeyValParams(
processor.extractParams || [],
defaultKeyValueParamItem
).validParams;
}
return processor;
});
return conditionCopy;
}
/** /**
* 生成请求参数 * 生成请求参数
* @param executeType 执行类型执行时传入 * @param executeType 执行类型执行时传入
@ -894,6 +883,7 @@
polymorphicName, polymorphicName,
}; };
} }
const { assertionConfig } = requestVModel.value.children[0];
return { return {
...requestParams, ...requestParams,
resourceId: requestVModel.value.resourceId, resourceId: requestVModel.value.resourceId,
@ -908,7 +898,10 @@
children: [ children: [
{ {
polymorphicName: 'MsCommonElement', // MsCommonElement polymorphicName: 'MsCommonElement', // MsCommonElement
assertionConfig: requestVModel.value.children[0].assertionConfig, assertionConfig: {
...requestVModel.value.children[0].assertionConfig,
assertions: filterAssertions(assertionConfig, isExecute),
},
postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig), postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig),
preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig), preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig),
}, },

View File

@ -8,7 +8,16 @@
:tip-content="t('apiScenario.openGlobalPreConditionTip')" :tip-content="t('apiScenario.openGlobalPreConditionTip')"
is-scenario is-scenario
@change="emit('change', true)" @change="emit('change', true)"
>
<template #dropdownAppend>
<a-tooltip :content="t('apiScenario.preConditionTip')" position="right">
<icon-question-circle
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]"
size="16"
/> />
</a-tooltip>
</template>
</precondition>
</div> </div>
<a-divider class="my-[8px]" type="dashed" /> <a-divider class="my-[8px]" type="dashed" />
<div> <div>
@ -20,7 +29,16 @@
:tip-content="t('apiScenario.openGlobalPostConditionTip')" :tip-content="t('apiScenario.openGlobalPostConditionTip')"
is-scenario is-scenario
@change="emit('change', false)" @change="emit('change', false)"
>
<template #dropdownAppend>
<a-tooltip :content="t('apiScenario.postConditionTip')" position="right">
<icon-question-circle
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]"
size="16"
/> />
</a-tooltip>
</template>
</postcondition>
</div> </div>
</div> </div>
</template> </template>

View File

@ -375,15 +375,15 @@
</a-form-item> </a-form-item>
</a-form> </a-form>
<template #footer> <template #footer>
<div class="flex items-center justify-between"> <div class="flex items-center justify-end">
<div class="flex items-center"> <!-- <div class="flex items-center">
<div class="text-[var(--color-text-4)]"> <div class="text-[var(--color-text-4)]">
{{ t('apiScenario.valuePriority') }} {{ t('apiScenario.valuePriority') }}
</div> </div>
<div v-if="scenarioConfigParamTip" class="text-[var(--color-text-1)]"> <div v-if="scenarioConfigParamTip" class="text-[var(--color-text-1)]">
{{ scenarioConfigParamTip }} {{ scenarioConfigParamTip }}
</div> </div>
</div> </div> -->
<div class="flex items-center gap-[12px]"> <div class="flex items-center gap-[12px]">
<a-button type="secondary" @click="cancelScenarioConfig">{{ t('common.cancel') }}</a-button> <a-button type="secondary" @click="cancelScenarioConfig">{{ t('common.cancel') }}</a-button>
<a-button type="primary" @click="saveScenarioConfig">{{ t('common.confirm') }}</a-button> <a-button type="primary" @click="saveScenarioConfig">{{ t('common.confirm') }}</a-button>

View File

@ -148,6 +148,7 @@
import { defaultScenario } from './components/config'; import { defaultScenario } from './components/config';
import updateStepStatus from './components/utils'; import updateStepStatus from './components/utils';
import { filterAssertions, filterConditionsSqlValidParams } from '@/views/api-test/components/utils';
// //
const detail = defineAsyncComponent(() => import('./detail/index.vue')); const detail = defineAsyncComponent(() => import('./detail/index.vue'));
@ -169,6 +170,7 @@
]); ]);
const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams); const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams);
const executeButtonRef = ref<InstanceType<typeof executeButton>>(); const executeButtonRef = ref<InstanceType<typeof executeButton>>();
const localExecuteUrl = computed(() => executeButtonRef.value?.localExecuteUrl);
const websocketMap: Record<string | number, WebSocket> = {}; const websocketMap: Record<string | number, WebSocket> = {};
@ -179,11 +181,11 @@
/** /**
* 开启websocket监听接收执行结果 * 开启websocket监听接收执行结果
*/ */
function debugSocket(scenario: Scenario, executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) { function debugSocket(scenario: Scenario, executeType?: 'localExec' | 'serverExec') {
websocketMap[scenario.reportId] = getSocket( websocketMap[scenario.reportId] = getSocket(
scenario.reportId || '', scenario.reportId || '',
executeType === 'localExec' ? '/ws/debug' : '', executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? localExecuteUrl : '' executeType === 'localExec' ? localExecuteUrl.value : ''
); );
websocketMap[scenario.reportId].addEventListener('message', (event) => { websocketMap[scenario.reportId].addEventListener('message', (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
@ -227,13 +229,12 @@
* @param executeParams 执行参数 * @param executeParams 执行参数
* @param isExecute 是否执行否则是调试 * @param isExecute 是否执行否则是调试
* @param executeType 执行类型 * @param executeType 执行类型
* @param localExecuteUrl 本地执行地址
*/ */
async function realExecute( async function realExecute(
executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId'>, executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId'>,
isExecute?: boolean, isExecute?: boolean,
executeType?: 'localExec' | 'serverExec', executeType?: 'localExec' | 'serverExec',
localExecuteUrl?: string envId?: string
) { ) {
try { try {
activeScenarioTab.value.executeLoading = true; activeScenarioTab.value.executeLoading = true;
@ -244,7 +245,7 @@
activeScenarioTab.value.executeFakeErrorCount = 0; activeScenarioTab.value.executeFakeErrorCount = 0;
activeScenarioTab.value.stepResponses = {}; activeScenarioTab.value.stepResponses = {};
activeScenarioTab.value.reportId = executeParams.reportId; // ID activeScenarioTab.value.reportId = executeParams.reportId; // ID
debugSocket(activeScenarioTab.value, executeType, localExecuteUrl); // websocket debugSocket(activeScenarioTab.value, executeType); // websocket
activeScenarioTab.value.isDebug = !isExecute; activeScenarioTab.value.isDebug = !isExecute;
let res; let res;
if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) { if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) {
@ -252,7 +253,7 @@
res = await executeScenario({ res = await executeScenario({
id: activeScenarioTab.value.id, id: activeScenarioTab.value.id,
grouped: false, grouped: false,
environmentId: appStore.getCurrentEnvId || '', environmentId: envId || appStore.getCurrentEnvId || '',
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
scenarioConfig: activeScenarioTab.value.scenarioConfig, scenarioConfig: activeScenarioTab.value.scenarioConfig,
...executeParams, ...executeParams,
@ -268,7 +269,7 @@
res = await debugScenario({ res = await debugScenario({
id: activeScenarioTab.value.id, id: activeScenarioTab.value.id,
grouped: false, grouped: false,
environmentId: appStore.getCurrentEnvId || '', environmentId: envId || appStore.getCurrentEnvId || '',
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
scenarioConfig: activeScenarioTab.value.scenarioConfig, scenarioConfig: activeScenarioTab.value.scenarioConfig,
stepFileParam: activeScenarioTab.value.stepFileParam, stepFileParam: activeScenarioTab.value.stepFileParam,
@ -282,9 +283,9 @@
}), }),
}); });
} }
if (executeType === 'localExec' && localExecuteUrl) { if (executeType === 'localExec' && localExecuteUrl.value) {
// debug // debug
await localExecuteApiDebug(localExecuteUrl, res); await localExecuteApiDebug(localExecuteUrl.value, res);
} }
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -298,9 +299,9 @@
/** /**
* 执行场景 * 执行场景
* @param executeType 执行类型 * @param executeType 执行类型
* @param localExecuteUrl 本地执行地址 * @param envId 环境ID
*/ */
function handleExecute(executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) { function handleExecute(executeType?: 'localExec' | 'serverExec', envId?: string) {
const waitingDebugStepDetails: Record<string, ScenarioStepDetails> = {}; const waitingDebugStepDetails: Record<string, ScenarioStepDetails> = {};
const waitTingDebugSteps = filterTree(activeScenarioTab.value.steps, (node) => { const waitTingDebugSteps = filterTree(activeScenarioTab.value.steps, (node) => {
if (node.enable) { if (node.enable) {
@ -323,7 +324,7 @@
}, },
true, true,
executeType, executeType,
localExecuteUrl envId
); );
} }
@ -431,7 +432,10 @@
if (action === 'execute') { if (action === 'execute') {
nextTick(() => { nextTick(() => {
// tab // tab
handleExecute(executeButtonRef.value?.isPriorityLocalExec ? 'localExec' : 'serverExec'); handleExecute(
executeButtonRef.value?.isPriorityLocalExec ? 'localExec' : 'serverExec',
defaultScenarioInfo.environmentId
);
}); });
} }
} else { } else {
@ -485,6 +489,7 @@
async function realSaveScenario() { async function realSaveScenario() {
try { try {
saveLoading.value = true; saveLoading.value = true;
const { assertionConfig } = activeScenarioTab.value.scenarioConfig;
if (activeScenarioTab.value.isNew) { if (activeScenarioTab.value.isNew) {
const res = await addScenario({ const res = await addScenario({
...activeScenarioTab.value, ...activeScenarioTab.value,
@ -494,6 +499,16 @@
parent: null, // axios parent: null, // axios
}; };
}), }),
scenarioConfig: {
...activeScenarioTab.value.scenarioConfig,
assertionConfig: { ...assertionConfig, assertions: filterAssertions(assertionConfig) },
postProcessorConfig: filterConditionsSqlValidParams(
activeScenarioTab.value.scenarioConfig.postProcessorConfig
),
preProcessorConfig: filterConditionsSqlValidParams(
activeScenarioTab.value.scenarioConfig.preProcessorConfig
),
},
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
environmentId: appStore.getCurrentEnvId || '', environmentId: appStore.getCurrentEnvId || '',
}); });
@ -535,6 +550,16 @@
} else { } else {
await updateScenario({ await updateScenario({
...activeScenarioTab.value, ...activeScenarioTab.value,
scenarioConfig: {
...activeScenarioTab.value.scenarioConfig,
assertionConfig: { ...assertionConfig, assertions: filterAssertions(assertionConfig) },
postProcessorConfig: filterConditionsSqlValidParams(
activeScenarioTab.value.scenarioConfig.postProcessorConfig
),
preProcessorConfig: filterConditionsSqlValidParams(
activeScenarioTab.value.scenarioConfig.preProcessorConfig
),
},
environmentId: appStore.getCurrentEnvId || '', environmentId: appStore.getCurrentEnvId || '',
steps: mapTree(activeScenarioTab.value.steps, (node) => { steps: mapTree(activeScenarioTab.value.steps, (node) => {
return { return {
@ -572,7 +597,10 @@
activeScenarioTab.value = scenarioTabs.value[isLoadedTabIndex]; activeScenarioTab.value = scenarioTabs.value[isLoadedTabIndex];
// tabid,id // tabid,id
if (action === 'execute') { if (action === 'execute') {
handleExecute(executeButtonRef.value?.isPriorityLocalExec ? 'localExec' : 'serverExec'); handleExecute(
executeButtonRef.value?.isPriorityLocalExec ? 'localExec' : 'serverExec',
typeof record === 'string' ? undefined : record.environmentId
);
} }
return; return;
} }
@ -605,7 +633,6 @@
useLeaveTabUnSaveCheck(scenarioTabs.value, ['PROJECT_API_SCENARIO:READ+ADD', 'PROJECT_API_SCENARIO:READ+UPDATE']); useLeaveTabUnSaveCheck(scenarioTabs.value, ['PROJECT_API_SCENARIO:READ+ADD', 'PROJECT_API_SCENARIO:READ+UPDATE']);
const hasLocalExec = computed(() => executeButtonRef.value?.hasLocalExec); const hasLocalExec = computed(() => executeButtonRef.value?.hasLocalExec);
const localExecuteUrl = computed(() => executeButtonRef.value?.localExecuteUrl);
const isPriorityLocalExec = computed(() => executeButtonRef.value?.isPriorityLocalExec); const isPriorityLocalExec = computed(() => executeButtonRef.value?.isPriorityLocalExec);
const scenarioId = computed(() => activeScenarioTab.value.id); const scenarioId = computed(() => activeScenarioTab.value.id);
const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading); const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading);

View File

@ -267,4 +267,6 @@ export default {
'apiScenario.schedule.table.tooltip.disable': 'Scheduled task is disabled', 'apiScenario.schedule.table.tooltip.disable': 'Scheduled task is disabled',
'apiScenario.save.env': 'Scenario Environment', 'apiScenario.save.env': 'Scenario Environment',
'apiScenario.execute.no.step.tips': 'No open step', 'apiScenario.execute.no.step.tips': 'No open step',
'apiScenario.preConditionTip': 'Execute once before the scene step',
'apiScenario.postConditionTip': 'Execute once after the scene step',
}; };

View File

@ -254,4 +254,6 @@ export default {
'apiScenario.schedule.table.tooltip.disable': '定时任务未开启', 'apiScenario.schedule.table.tooltip.disable': '定时任务未开启',
'apiScenario.save.env': '场景保存的环境', 'apiScenario.save.env': '场景保存的环境',
'apiScenario.execute.no.step.tips': '没有开启的步骤', 'apiScenario.execute.no.step.tips': '没有开启的步骤',
'apiScenario.preConditionTip': '在场景步骤前分别执行一次',
'apiScenario.postConditionTip': '在场景步骤后分别执行一次',
}; };