feat(接口场景): 场景单步骤执行&自定义 api、引用 api、复制 api、插件抽屉执行

This commit is contained in:
baiqi 2024-03-26 22:25:44 +08:00 committed by Craftsman
parent 594a95d26e
commit 451265b018
9 changed files with 215 additions and 174 deletions

View File

@ -33,29 +33,33 @@
<slot name="title" v-bind="_props"></slot>
</template>
<template v-if="$slots['extra'] || props.nodeMoreActions" #extra="_props">
<div v-if="_props.hideMoreAction !== true" class="ms-tree-node-extra">
<slot name="extra" v-bind="_props"></slot>
<MsTableMoreAction
v-if="props.nodeMoreActions"
:list="
typeof props.filterMoreActionFunc === 'function'
? props.filterMoreActionFunc(props.nodeMoreActions, _props)
: props.nodeMoreActions
"
trigger="click"
@select="handleNodeMoreSelect($event, _props)"
@close="moreActionsClose"
>
<MsButton
type="text"
:size="props.nodeMoreActionSize || 'mini'"
class="ms-tree-node-extra__more"
@click="focusNodeKey = _props[props.fieldNames.key]"
<div class="sticky right-0 flex items-center justify-between">
<div v-if="_props.hideMoreAction !== true" class="ms-tree-node-extra">
<slot name="extra" v-bind="_props"></slot>
<MsTableMoreAction
v-if="props.nodeMoreActions"
:list="
typeof props.filterMoreActionFunc === 'function'
? props.filterMoreActionFunc(props.nodeMoreActions, _props)
: props.nodeMoreActions
"
trigger="click"
@select="handleNodeMoreSelect($event, _props)"
@close="moreActionsClose"
>
<MsIcon type="icon-icon_more_outlined" size="14" class="text-[var(--color-text-4)]" />
</MsButton>
</MsTableMoreAction>
<slot name="extraEnd" v-bind="_props"></slot>
<MsButton
type="text"
:size="props.nodeMoreActionSize || 'mini'"
class="ms-tree-node-extra__more"
@click="focusNodeKey = _props[props.fieldNames.key]"
>
<MsIcon type="icon-icon_more_outlined" size="14" class="text-[var(--color-text-4)]" />
</MsButton>
</MsTableMoreAction>
</div>
<div class="ms-tree-node-extra-end">
<slot name="extraEnd" v-bind="_props"></slot>
</div>
</div>
</template>
</a-tree>
@ -412,6 +416,9 @@
}
}
}
.ms-tree-node-extra {
@apply visible w-auto;
}
}
.arco-tree-node-indent-block {
width: 1px;
@ -459,7 +466,7 @@
width: 60%;
}
.ms-tree-node-extra {
@apply invisible relative sticky right-0 flex w-0 items-center;
@apply invisible flex w-0 items-center;
margin-left: -4px;
height: 32px;
@ -483,6 +490,9 @@
margin-right: 4px;
}
}
.ms-tree-node-extra-end {
@apply flex items-center;
}
.arco-tree-node-custom-icon {
@apply hidden;
}

View File

@ -342,6 +342,8 @@ export interface ScenarioStepItem {
resourceName?: string; // 引用复制接口、用例、场景时的源资源名称
method?: RequestMethods;
executeStatus?: ScenarioExecuteStatus;
isExecuting?: boolean; // 是否正在执行
reportId?: string | number; // 步骤单个调试时的报告id
}
// 场景
export interface Scenario {

View File

@ -62,6 +62,7 @@ export interface EnvConfig {
postProcessorConfig: ProcessorConfig;
assertionConfig: AssertionConfig;
pluginConfigMap: EnvConfigItem;
name?: string;
}
export interface EnvDetailItem {
id?: string;

View File

@ -43,6 +43,7 @@
currentEnvConfig.value = {
...res,
id: currentEnv.value,
name: envOptions.value.find((item) => item.value === currentEnv.value)?.label || '',
};
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -22,11 +22,8 @@
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
class="ml-auto flex items-center gap-[16px]"
>
<div
v-show="!requestVModel.customizeRequestEnvEnable"
class="text-[14px] font-normal text-[var(--color-text-4)]"
>
{{ t('apiScenario.env', { name: props.envDetailItem?.name }) }}
<div class="text-[14px] font-normal text-[var(--color-text-4)]">
{{ t('apiScenario.env', { name: currentEnvConfig?.name }) }}
</div>
<a-select
v-model:model-value="requestVModel.customizeRequestEnvEnable"
@ -300,7 +297,6 @@
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/common';
import { getDefinitionDetail } from '@/api/modules/api-test/management';
import { getTransferOptions, transferFile, uploadTempFile } from '@/api/modules/api-test/scenario';
import { getSocket } from '@/api/modules/project-management/commonScript';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import { getGenerateId, parseQueryParams } from '@/utils';
@ -309,12 +305,12 @@
import {
ExecuteApiRequestFullParams,
ExecuteConditionConfig,
ExecuteRequestParams,
PluginConfig,
RequestResult,
RequestTaskResult,
} from '@/models/apiTest/common';
import { ScenarioStepItem } from '@/models/apiTest/scenario';
import { EnvConfig } from '@/models/projectManagement/environmental';
import {
RequestAuthType,
RequestBodyFormat,
@ -372,13 +368,6 @@
request?: RequestParam; //
step?: ScenarioStepItem;
detailLoading?: boolean; //
envDetailItem?: {
id?: string;
projectId: string;
name: string;
};
executeApi?: (params: ExecuteRequestParams) => Promise<any>; //
localExecuteApi?: (url: string, params: ExecuteRequestParams) => Promise<any>; //
permissionMap?: {
execute: string;
create: string;
@ -390,6 +379,8 @@
const emit = defineEmits<{
(e: 'addStep', request: RequestParam): void;
(e: 'applyStep', request: RequestParam): void;
(e: 'execute', request: RequestParam, executeType?: 'localExec' | 'serverExec'): void;
(e: 'stopDebug'): void;
}>();
const appStore = useAppStore();
@ -397,6 +388,8 @@
//
const scenarioId = inject<string | number>('scenarioId');
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
const isPriorityLocalExec = inject<Ref<boolean>>('isPriorityLocalExec');
const visible = defineModel<boolean>('visible', { required: true });
const loading = defineModel<boolean>('detailLoading', { default: false });
@ -477,13 +470,25 @@
}
return t('apiScenario.customApi');
});
watch(
() => props.stepResponses,
(val) => {
if (val) {
requestVModel.value.executeLoading = false;
}
},
{
deep: true,
}
);
// api props.request
const isCopyApiNeedInit = computed(() => _stepType.value.isCopyApi && props.request === undefined);
const isEditableApi = computed(
() => _stepType.value.isCopyApi || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST || !props.step
);
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
const temporaryResponseMap = {}; // websockettab
const isInitPluginForm = ref(false);
function handleActiveDebugChange() {
@ -639,8 +644,6 @@
}
const hasLocalExec = ref(false); // api
const isPriorityLocalExec = ref(false); //
const localExecuteUrl = ref('');
const pluginScriptMap = ref<Record<string, PluginConfig>>({}); //
const temporaryPluginFormMap: Record<string, any> = {}; // API
@ -865,38 +868,6 @@
return conditionCopy;
}
const websocket = ref<WebSocket>();
/**
* 开启websocket监听接收执行结果
*/
function debugSocket(executeType?: 'localExec' | 'serverExec') {
websocket.value = getSocket(
requestVModel.value.stepId,
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? localExecuteUrl.value : ''
);
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (requestVModel.value.stepId === data.reportId) {
// tabtab
requestVModel.value.response = data.taskResult;
requestVModel.value.executeLoading = false;
requestVModel.value.isExecute = false;
} else {
// tab
temporaryResponseMap[data.reportId] = data.taskResult;
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
requestVModel.value.executeLoading = false;
requestVModel.value.isExecute = false;
}
});
}
/**
* 生成请求参数
* @param executeType 执行类型执行时传入
@ -950,9 +921,6 @@
polymorphicName,
};
}
if (isExecute) {
debugSocket(executeType); // websocket
}
return {
...requestParams,
resourceId: requestVModel.value.resourceId,
@ -971,6 +939,7 @@
preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig),
},
],
executeLoading: isExecute,
...parseRequestBodyResult,
};
}
@ -980,38 +949,14 @@
* @param val 执行类型
*/
async function execute(executeType?: 'localExec' | 'serverExec') {
requestVModel.value.executeLoading = true;
if (isHttpProtocol.value) {
try {
if (!props.executeApi) return;
requestVModel.value.executeLoading = true;
requestVModel.value.response = cloneDeep(defaultResponse);
const res = await props.executeApi(makeRequestParams(executeType));
if (executeType === 'localExec' && props.localExecuteApi) {
await props.localExecuteApi(localExecuteUrl.value, res);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
requestVModel.value.executeLoading = false;
}
emit('execute', makeRequestParams(executeType), executeType);
} else {
//
fApi.value?.validate(async (valid) => {
if (valid === true) {
try {
if (!props.executeApi) return;
requestVModel.value.executeLoading = true;
requestVModel.value.response = cloneDeep(defaultResponse);
const res = await props.executeApi(makeRequestParams(executeType));
if (executeType === 'localExec' && props.localExecuteApi) {
await props.localExecuteApi(localExecuteUrl.value, res);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
requestVModel.value.executeLoading = false;
}
emit('execute', makeRequestParams(executeType), executeType);
} else {
requestVModel.value.activeTab = RequestComposition.PLUGIN;
nextTick(() => {
@ -1023,8 +968,7 @@
}
function stopDebug() {
websocket.value?.close();
requestVModel.value.executeLoading = false;
emit('stopDebug');
}
function handleContinue() {
@ -1067,6 +1011,7 @@
url: res.path,
name: res.name, // requestnamenull
resourceId: res.id,
stepId: props.step?.id || '',
...parseRequestBodyResult,
};
if (_stepType.value.isQuoteApi && props.request && isHttpProtocol.value) {

View File

@ -64,6 +64,7 @@ export const defaultStepItemCommon = {
},
createActionsVisible: false,
responsePopoverVisible: false,
isExecuting: false,
};
export const defaultScenario: Scenario = {

View File

@ -43,43 +43,45 @@
</a-button>
</template>
</div>
<template v-if="scenario.executeTime">
<div class="action-group">
<div class="text-[var(--color-text-4)]">{{ t('apiScenario.executeTime') }}</div>
<div class="text-[var(--color-text-4)]">{{ scenario.executeTime }}</div>
</div>
<div class="action-group">
<div class="text-[var(--color-text-4)]">{{ t('apiScenario.executeResult') }}</div>
<div class="flex items-center gap-[4px]">
<div class="text-[var(--color-text-1)]">{{ t('common.success') }}</div>
<div class="text-[rgb(var(--success-6))]">{{ scenario.executeSuccessCount }}</div>
<div class="action-group ml-auto">
<template v-if="scenario.executeTime">
<div class="action-group">
<div class="text-[var(--color-text-4)]">{{ t('apiScenario.executeTime') }}</div>
<div class="text-[var(--color-text-4)]">{{ scenario.executeTime }}</div>
</div>
<div class="flex items-center gap-[4px]">
<div class="text-[var(--color-text-1)]">{{ t('common.fail') }}</div>
<div class="text-[rgb(var(--success-6))]">{{ scenario.executeFailCount }}</div>
<div class="action-group">
<div class="text-[var(--color-text-4)]">{{ t('apiScenario.executeResult') }}</div>
<div class="flex items-center gap-[4px]">
<div class="text-[var(--color-text-1)]">{{ t('common.success') }}</div>
<div class="text-[rgb(var(--success-6))]">{{ scenario.executeSuccessCount }}</div>
</div>
<div class="flex items-center gap-[4px]">
<div class="text-[var(--color-text-1)]">{{ t('common.fail') }}</div>
<div class="text-[rgb(var(--success-6))]">{{ scenario.executeFailCount }}</div>
</div>
<MsButton v-if="scenario.isDebug === false" type="text" @click="checkReport">
{{ t('apiScenario.checkReport') }}
</MsButton>
</div>
<MsButton v-if="scenario.isDebug === false" type="text" @click="checkReport">
{{ t('apiScenario.checkReport') }}
</MsButton>
</template>
<div v-if="!checkedAll && !indeterminate" class="action-group ml-auto">
<a-input
v-model:model-value="keyword"
:placeholder="t('apiScenario.searchByName')"
allow-clear
class="w-[200px]"
/>
<a-button
v-if="!props.isNew"
type="outline"
class="arco-btn-outline--secondary !mr-0 !p-[8px]"
@click="refreshStepInfo"
>
<template #icon>
<icon-refresh class="text-[var(--color-text-4)]" />
</template>
</a-button>
</div>
</template>
<div v-if="!checkedAll && !indeterminate" class="action-group ml-auto">
<a-input
v-model:model-value="keyword"
:placeholder="t('apiScenario.searchByName')"
allow-clear
class="w-[200px]"
/>
<a-button
v-if="!props.isNew"
type="outline"
class="arco-btn-outline--secondary !mr-0 !p-[8px]"
@click="refreshStepInfo"
>
<template #icon>
<icon-refresh class="text-[var(--color-text-4)]" />
</template>
</a-button>
</div>
</div>
<div class="h-[calc(100%-30px)]">
@ -91,7 +93,6 @@
v-model:scenario="scenario"
:expand-all="isExpandAll"
:step-details="scenario.stepDetails"
:step-responses="scenario.stepResponses"
@update-resource="handleUpdateResource"
/>
</div>

View File

@ -178,8 +178,8 @@
<template #content>
<responseResult
:active-tab="ResponseComposition.BODY"
:request-result="props.stepResponses?.[step.id]"
:console="props.stepResponses?.[step.id]?.console"
:request-result="scenario.stepResponses?.[step.id]"
:console="scenario.stepResponses?.[step.id]?.console"
:show-empty="false"
:is-edit="false"
is-definition
@ -215,18 +215,19 @@
</createStepActions>
<customApiDrawer
v-model:visible="customApiDrawerVisible"
:env-detail-item="{ id: 'demp-id-112233', projectId: '123456', name: 'demo环境' }"
:request="currentStepDetail"
:step="activeStep"
:step-responses="props.stepResponses"
:step-responses="scenario.stepResponses"
@add-step="addCustomApiStep"
@apply-step="applyApiStep"
@stop-debug="handleStopExecute"
@execute="handleApiExecute"
/>
<customCaseDrawer
v-model:visible="customCaseDrawerVisible"
:active-step="activeStep"
:request="currentStepDetail"
:step-responses="props.stepResponses"
:step-responses="scenario.stepResponses"
@apply-step="applyApiStep"
@delete-step="deleteCaseStep"
/>
@ -278,7 +279,6 @@
import { useEventListener } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
@ -312,7 +312,7 @@
TreeNode,
} from '@/utils';
import { ExecuteConditionProcessor, RequestResult } from '@/models/apiTest/common';
import { ExecuteConditionProcessor } from '@/models/apiTest/common';
import { ApiScenarioDebugRequest, CreateStepAction, Scenario, ScenarioStepItem } from '@/models/apiTest/scenario';
import { EnvConfig } from '@/models/projectManagement/environmental';
import {
@ -338,7 +338,6 @@
const props = defineProps<{
stepKeyword: string;
expandAll?: boolean;
stepResponses?: Record<string | number, RequestResult>;
}>();
const emit = defineEmits<{
(e: 'updateResource', uploadFileIds: string[], linkFileIds: string[]): void;
@ -360,6 +359,7 @@
const scenario = defineModel<Scenario>('scenario', {
required: true,
});
const isPriorityLocalExec = inject<Ref<boolean>>('isPriorityLocalExec');
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
const selectedKeys = ref<(string | number)[]>([]); //
@ -372,8 +372,10 @@
}
function getExecuteStatus(step: ScenarioStepItem) {
if (props.stepResponses && props.stepResponses[step.id]) {
return props.stepResponses[step.id].isSuccessful ? ScenarioExecuteStatus.SUCCESS : ScenarioExecuteStatus.FAILED;
if (scenario.value.stepResponses && scenario.value.stepResponses[step.id]) {
return scenario.value.stepResponses[step.id].isSuccessful
? ScenarioExecuteStatus.SUCCESS
: ScenarioExecuteStatus.FAILED;
}
return step.executeStatus;
}
@ -692,12 +694,31 @@
}
const websocket = ref<WebSocket>();
const temporaryScenarioReportMap = {}; // websockettab
let temporaryStepReportMap = {}; // websockettab
watch(
() => scenario.value.id,
() => {
const stepKeys = Object.keys(temporaryStepReportMap);
if (stepKeys.length > 0) {
stepKeys.forEach((key) => {
const report = temporaryStepReportMap[key];
scenario.value.stepResponses[report.stepId] = temporaryStepReportMap[key];
});
temporaryStepReportMap = {};
}
}
);
/**
* 开启websocket监听接收执行结果
*/
function debugSocket(reportId?: string | number, executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
function debugSocket(
step: ScenarioStepItem,
reportId?: string | number,
executeType?: 'localExec' | 'serverExec',
localExecuteUrl?: string
) {
websocket.value = getSocket(
reportId || '',
executeType === 'localExec' ? '/ws/debug' : '',
@ -706,27 +727,22 @@
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (scenario.value.reportId === data.reportId) {
if (step.reportId === data.reportId) {
// tabtab
data.taskResult.requestResults.forEach((result) => {
scenario.value.stepResponses[result.stepId] = {
...result,
console: data.taskResult.console,
};
if (result.isSuccessful) {
scenario.value.executeSuccessCount += 1;
} else {
scenario.value.executeFailCount += 1;
}
});
} else {
// tab
data.taskResult.requestResults.forEach((result) => {
if (scenario.value.reportId) {
if (temporaryScenarioReportMap[scenario.value.reportId] === undefined) {
temporaryScenarioReportMap[scenario.value.reportId] = {};
if (step.reportId) {
if (temporaryStepReportMap[step.reportId] === undefined) {
temporaryStepReportMap[step.reportId] = {};
}
temporaryScenarioReportMap[scenario.value.reportId][result.stepId] = {
temporaryStepReportMap[step.reportId] = {
...result,
console: data.taskResult.console,
};
@ -736,30 +752,31 @@
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
if (scenario.value.reportId === data.reportId) {
scenario.value.executeLoading = false;
scenario.value.isExecute = false;
if (step.reportId === data.reportId) {
step.isExecuting = false;
}
}
});
}
async function realExecute(
executeParams: Pick<ApiScenarioDebugRequest, 'steps' | 'stepDetails' | 'reportId'>,
executeParams: Pick<
ApiScenarioDebugRequest,
'steps' | 'stepDetails' | 'reportId' | 'uploadFileIds' | 'linkFileIds'
>,
executeType?: 'localExec' | 'serverExec',
localExecuteUrl?: string
) {
const [currentStep] = executeParams.steps;
try {
scenario.value.executeLoading = true;
debugSocket(executeParams.reportId, executeType, localExecuteUrl); // websocket
currentStep.isExecuting = true;
debugSocket(currentStep, executeParams.reportId, executeType, localExecuteUrl); // websocket
const res = await debugScenario({
id: scenario.value.id || '',
grouped: false,
environmentId: currentEnvConfig?.value.id || '',
projectId: appStore.currentProjectId,
scenarioConfig: scenario.value.scenarioConfig,
uploadFileIds: scenario.value.uploadFileIds,
linkFileIds: scenario.value.linkFileIds,
frontendDebug: executeType === 'localExec',
...executeParams,
});
@ -770,15 +787,71 @@
// eslint-disable-next-line no-console
console.log(error);
websocket.value?.close();
scenario.value.executeLoading = false;
currentStep.isExecuting = false;
}
}
/**
* 单个步骤执行调试
*/
function executeStep(node: MsTreeNodeData) {
if (scenario.value.executeLoading) {
if (node.isExecuting) {
return;
}
console.log('执行步骤', node);
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, node.id, 'id');
if (realStep) {
realStep.reportId = getGenerateId();
realStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
const stepDetail = stepDetails.value[realStep.id];
delete scenario.value.stepResponses[realStep.id]; //
realExecute(
{
steps: [realStep as ScenarioStepItem],
stepDetails: {
[realStep.id]: stepDetails.value[realStep.id],
},
reportId: realStep.reportId,
uploadFileIds: stepDetail?.uploadFileIds || [],
linkFileIds: stepDetail?.linkFileIds || [],
},
isPriorityLocalExec?.value ? 'localExec' : 'serverExec'
);
}
}
/**
* 处理 api 详情抽屉的执行动作
* @param request 抽屉内的请求参数
* @param executeType 执行类型
*/
function handleApiExecute(request: RequestParam, executeType?: 'localExec' | 'serverExec') {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, request.stepId, 'id');
if (realStep) {
delete scenario.value.stepResponses[realStep.id]; //
realStep.reportId = getGenerateId();
realStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
request.executeLoading = true;
realExecute(
{
steps: [realStep as ScenarioStepItem],
stepDetails: {
[realStep.id]: request,
},
reportId: realStep.reportId,
uploadFileIds: request.uploadFileIds || [],
linkFileIds: request.linkFileIds || [],
},
executeType
);
}
}
function handleStopExecute() {
websocket.value?.close();
if (activeStep.value) {
activeStep.value.isExecuting = false;
activeStep.value.executeStatus = undefined;
}
}
/**

View File

@ -21,6 +21,7 @@
{{ t('common.save') }}
</a-button>
<executeButton
ref="executeButtonRef"
:execute-loading="activeScenarioTab.executeLoading"
is-emit
@execute="handleExecute"
@ -172,6 +173,8 @@
const activeFolder = ref<string>('all');
const offspringIds = ref<string[]>([]);
const isShowScenario = ref(false);
const executeButtonRef = ref<InstanceType<typeof executeButton>>();
const currentEnvConfig = ref<EnvConfig>();
//
const getActiveClass = (type: string) => {
@ -221,6 +224,7 @@
const res = await addScenario({
...activeScenarioTab.value,
projectId: appStore.currentProjectId,
environmentId: currentEnvConfig.value?.id || '',
});
const scenarioDetail = await getScenarioDetail(res.id);
scenarioDetail.stepDetails = {};
@ -241,6 +245,7 @@
} else {
await updateScenario({
...activeScenarioTab.value,
environmentId: currentEnvConfig.value?.id || '',
});
}
Message.success(activeScenarioTab.value.isNew ? t('common.createSuccess') : t('common.saveSuccess'));
@ -281,7 +286,6 @@
}
}
const currentEnvConfig = ref<EnvConfig>();
const websocket = ref<WebSocket>();
const temporaryScenarioReportMap = {}; // websockettab
@ -352,7 +356,8 @@
activeScenarioTab.value.reportId = executeParams.reportId; // ID
activeScenarioTab.value.isDebug = !isExecute;
let res;
if (isExecute && executeType !== 'localExec') {
if (isExecute && executeType !== 'localExec' && !activeScenarioTab.value.isNew) {
//
res = await executeScenario({
id: activeScenarioTab.value.id,
grouped: false,
@ -437,9 +442,11 @@
}
);
const isPriorityLocalExec = computed(() => executeButtonRef.value?.isPriorityLocalExec);
const scenarioId = computed(() => activeScenarioTab.value.id);
const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading);
//
provide('isPriorityLocalExec', readonly(isPriorityLocalExec));
provide('currentEnvConfig', readonly(currentEnvConfig));
provide('scenarioId', scenarioId);
provide('scenarioExecuteLoading', scenarioExecuteLoading);