style(接口测试): 使用Prettier统一按照标准格式化

This commit is contained in:
fit2-zhao 2022-11-21 12:47:19 +08:00 committed by fit2-zhao
parent c1705b48c9
commit db47ab5f5b
414 changed files with 41369 additions and 23889 deletions

View File

@ -1,3 +1,3 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"]
presets: ['@vue/cli-plugin-babel/preset'],
};

View File

@ -1,18 +1,21 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -1,5 +1,5 @@
<template>
<div id="app">
<router-view/>
<router-view />
</div>
</template>

View File

@ -1,4 +1,4 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import { get, post } from 'metersphere-frontend/src/plugins/request';
const BASE_URL = '/environment/';

View File

@ -1,5 +1,5 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import {fileUpload} from "@/api/base-network";
import { get, post } from 'metersphere-frontend/src/plugins/request';
import { fileUpload } from '@/api/base-network';
export function getMockApiParams(id) {
return get('/mock/config/get-api-params/' + id);
@ -33,7 +33,7 @@ export function getMockTestData(mockParam) {
return post('/mock/config/test-data', mockParam);
}
export function updateMockExpectConfig(mockParam, file, files,) {
export function updateMockExpectConfig(mockParam, file, files) {
return fileUpload('/mock/config/update/form', file, files, mockParam);
}

View File

@ -1,4 +1,4 @@
import {get} from 'metersphere-frontend/src/plugins/request'
import { get } from 'metersphere-frontend/src/plugins/request';
export function getApiTemplate(projectId) {
return new Promise((resolve) => {
@ -7,7 +7,7 @@ export function getApiTemplate(projectId) {
get(baseUrl + projectId).then((response) => {
template = response.data;
if (template.customFields) {
template.customFields.forEach(item => {
template.customFields.forEach((item) => {
if (item.options) {
item.options = JSON.parse(item.options);
}

View File

@ -1,5 +1,5 @@
import {fileUpload} from "@/api/base-network";
import {get, post} from "metersphere-frontend/src/plugins/request"
import { fileUpload } from '@/api/base-network';
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function editApiTestCaseOrder(request) {
return post('/api/testcase/sort', request);
@ -83,9 +83,9 @@ export function editApiCase(url, file, files, params) {
}
export function apiTestCaseCount(param) {
return post('/api/testcase/case-count',param);
return post('/api/testcase/case-count', param);
}
export function getApiCaseEnvironments(param) {
return post('/api/testcase/get/env',param);
return post('/api/testcase/get/env', param);
}

View File

@ -1,26 +1,32 @@
import {request, socket} from "metersphere-frontend/src/plugins/request"
import jsFileDownload from 'js-file-download'
import {$error} from "metersphere-frontend/src/plugins/message"
import { request, socket } from 'metersphere-frontend/src/plugins/request';
import jsFileDownload from 'js-file-download';
import { $error } from 'metersphere-frontend/src/plugins/message';
export function getUploadConfig(url, formData) {
return {
method: 'POST', url: url, data: formData, headers: {
'Content-Type': undefined
}
method: 'POST',
url: url,
data: formData,
headers: {
'Content-Type': undefined,
},
};
}
export function fileUpload(url, file, files, param) {
let formData = new FormData();
if (file) {
formData.append("file", file);
formData.append('file', file);
}
if (files) {
files.forEach(f => {
formData.append("files", f);
files.forEach((f) => {
formData.append('files', f);
});
}
formData.append('request', new Blob([JSON.stringify(param)], {type: "application/json"}));
formData.append(
'request',
new Blob([JSON.stringify(param)], { type: 'application/json' })
);
let config = getUploadConfig(url, formData);
return request(config);
}
@ -35,15 +41,18 @@ export function downloadFile(method, url, data, fileName) {
method: method,
data: data,
responseType: 'blob',
headers: {"Content-Type": "application/json; charset=utf-8"}
headers: { 'Content-Type': 'application/json; charset=utf-8' },
};
request(config)
.then((res) => {
fileName = fileName ? fileName : window.decodeURI(res.headers['content-disposition'].split('=')[1]);
fileName = fileName
? fileName
: window.decodeURI(res.headers['content-disposition'].split('=')[1]);
jsFileDownload(res.data, fileName);
}).catch((e) => {
$error(e.message);
})
})
.catch((e) => {
$error(e.message);
});
}
export function baseSocket(url) {

View File

@ -1,4 +1,4 @@
import {get, post} from 'metersphere-frontend/src/plugins/request'
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function getFuncById(id) {
return get('/custom/func/get/' + id);

View File

@ -1,7 +1,12 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function getApiModules(projectId, protocol, currentVersion) {
let url = '/api/module/list/' + projectId + '/' + protocol + (currentVersion ? '/' + currentVersion : '');
let url =
'/api/module/list/' +
projectId +
'/' +
protocol +
(currentVersion ? '/' + currentVersion : '');
return get(url);
}
@ -11,7 +16,13 @@ export function getApiModuleByProjectIdAndProtocol(projectId, protocol) {
}
export function getApiModuleByTrash(projectId, protocol, currentVersion) {
let url = '/api/module/trash/list/' + projectId + '/' + protocol + '/' + (currentVersion ? '/' + currentVersion : '');
let url =
'/api/module/trash/list/' +
projectId +
'/' +
protocol +
'/' +
(currentVersion ? '/' + currentVersion : '');
return get(url);
}
@ -44,5 +55,3 @@ export function dragModule(param) {
export function posModule(param) {
return post('/api/module/pos', param);
}

View File

@ -1,4 +1,4 @@
import {post, get} from 'metersphere-frontend/src/plugins/request'
import { post, get } from 'metersphere-frontend/src/plugins/request';
export function getApiReportPage(currentPage, pageSize, condition) {
let url = '/api/testcase/list-execute-res/' + currentPage + '/' + pageSize;

View File

@ -1,5 +1,5 @@
import {fileDownload, fileUpload} from "@/api/base-network";
import {get, post} from "metersphere-frontend/src/plugins/request"
import { fileDownload, fileUpload } from '@/api/base-network';
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function getRelationshipCountApi(id) {
return get('/api/definition/relationship/count/' + id);
@ -62,11 +62,16 @@ export function apiListBatch(params) {
}
export function definitionWeekList(projectId, page, pageSize) {
return post('/api/definition/list/week/' + projectId + '/' + page + '/' + pageSize);
return post(
'/api/definition/list/week/' + projectId + '/' + page + '/' + pageSize
);
}
export function getRelevanceDefinitionPage(page, pageSize, params) {
return post('/api/definition/list/relevance/' + page + '/' + pageSize, params);
return post(
'/api/definition/list/relevance/' + page + '/' + pageSize,
params
);
}
export function getDefinitionPage(page, pageSize, params) {
@ -110,7 +115,10 @@ export function definitionReduction(params) {
}
export function definitionRelationship(currentPage, pageSize, params) {
return post('/api/definition/relationship/relate/' + currentPage + '/' + pageSize, params);
return post(
'/api/definition/relationship/relate/' + currentPage + '/' + pageSize,
params
);
}
export function addRelationship(params) {
@ -122,11 +130,17 @@ export function jsonGenerator(params) {
}
export function getDefinitionReference(currentPage, pageSize, params) {
return post('/api/definition/get-reference/' + currentPage + '/' + pageSize, params);
return post(
'/api/definition/get-reference/' + currentPage + '/' + pageSize,
params
);
}
export function getPlanReference(currentPage, pageSize, params) {
return post('/test/plan/api/case/get-reference/' + currentPage + '/' + pageSize, params);
return post(
'/test/plan/api/case/get-reference/' + currentPage + '/' + pageSize,
params
);
}
export function deleteBatchByParams(params) {

View File

@ -1,10 +1,9 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function getRelationshipGraph(id, type) {
return get('/graph/relationship/graph/' + id + '/' + type);
}
export function getGraphByCondition(relationshipType, param) {
return post('/graph/relationship/graph/condition/' + relationshipType, param);
}

View File

@ -1,5 +1,5 @@
import {fileUpload} from "@/api/base-network";
import {get, post} from "metersphere-frontend/src/plugins/request"
import { fileUpload } from '@/api/base-network';
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function apiCountByProjectId(projectId) {
return get('/home/api/count/' + projectId);
@ -17,7 +17,6 @@ export function scheduleTaskCountByProjectId(projectId) {
return get('/home/schedule/task/count/' + projectId);
}
export function dubboProviders(params) {
return post('/home/api/dubbo/providers', params);
}
@ -32,11 +31,20 @@ export function genPerformanceTestXml(file, files, params) {
}
export function getRunningTask(selectProjectId, currentPage, pageSize, param) {
return post("/task/center/runningTask/" + selectProjectId + "/" + currentPage + "/" + pageSize, param);
return post(
'/task/center/runningTask/' +
selectProjectId +
'/' +
currentPage +
'/' +
pageSize,
param
);
}
export function formatNumber(param) {
let num = (param || 0).toString(), result = '';
let num = (param || 0).toString(),
result = '';
while (num.length > 3) {
result = ',' + num.slice(-3) + result;
num = num.slice(0, num.length - 3);

View File

@ -1,8 +1,6 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function uploadMarkDownImg(file) {
}
export function uploadMarkDownImg(file) {}
export function deleteMarkDownImg(file) {
if (file) {

View File

@ -1,4 +1,4 @@
import {post, get} from 'metersphere-frontend/src/plugins/request'
import { post, get } from 'metersphere-frontend/src/plugins/request';
export function getMessageById(id) {
let url = '/notice/search/message/' + id;

View File

@ -1,4 +1,4 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function getPlugin(id) {
return get('/plugin/get/' + id);

View File

@ -1,5 +1,5 @@
import {get, post} from 'metersphere-frontend/src/plugins/request'
import {getCurrentWorkspaceId} from 'metersphere-frontend/src/utils/token'
import { get, post } from 'metersphere-frontend/src/plugins/request';
import { getCurrentWorkspaceId } from 'metersphere-frontend/src/utils/token';
export function getProject(projectId) {
let url = '/project/get/' + projectId;
@ -11,7 +11,8 @@ export function getOwnerProjectIds() {
return get(url);
}
export function getMaintainer() {getAll
export function getMaintainer() {
getAll;
let url = '/user/project/member/list';
return get(url);
}

View File

@ -1,4 +1,4 @@
import {post, get} from 'metersphere-frontend/src/plugins/request'
import { post, get } from 'metersphere-frontend/src/plugins/request';
export function getModuleByProjectId(projectId) {
let url = '/api/automation/module/list/' + projectId;

View File

@ -1,4 +1,4 @@
import {post, get} from 'metersphere-frontend/src/plugins/request'
import { post, get } from 'metersphere-frontend/src/plugins/request';
export function getReportPage(currentPage, pageSize, condition) {
let url = '/api/scenario/report/list/' + currentPage + '/' + pageSize;
@ -12,7 +12,7 @@ export function getReportPageDetail(currentPage, pageSize, condition) {
export function delReport(id) {
let url = '/api/scenario/report/delete';
return post(url, {id: id});
return post(url, { id: id });
}
export function delBatchReport(condition) {

View File

@ -1,5 +1,5 @@
import {fileUpload} from "@/api/base-network";
import {get, post, request} from 'metersphere-frontend/src/plugins/request'
import { fileUpload } from '@/api/base-network';
import { get, post, request } from 'metersphere-frontend/src/plugins/request';
export function getScenarioById(scenarioId) {
return get('/api/automation/get/' + scenarioId);

View File

@ -1,4 +1,4 @@
import {post, get} from 'metersphere-frontend/src/plugins/request'
import { post, get } from 'metersphere-frontend/src/plugins/request';
export function getScheduleByIdAndType(scheduleResourceID, taskType) {
let url = '/api/schedule/get/' + scheduleResourceID + '/' + taskType;

View File

@ -1,24 +1,30 @@
import {post, generateShareUrl, get} from "metersphere-frontend/src/plugins/request"
import {
post,
generateShareUrl,
get,
} from 'metersphere-frontend/src/plugins/request';
export function generateApiDocumentShareInfo(param) {
return post("/share/generate/api/document", param);
return post('/share/generate/api/document', param);
}
export function selectApiInfoByParam(param, currentPage, pageSize) {
return post("/share/list/" + currentPage + "/" + pageSize, param);
return post('/share/list/' + currentPage + '/' + pageSize, param);
}
export function generateShareInfoWithExpired(param) {
return post("/share/generate/expired", param);
return post('/share/generate/expired', param);
}
export function getShareContent(shareId, stepId) {
let url = "/share/" + shareId + "/scenario/report/detail/" + stepId;
let url = '/share/' + shareId + '/scenario/report/detail/' + stepId;
return get(url);
}
export function getShareApiReport(shareId, testId) {
return get('/share/api/definition/report/getReport/' + shareId + '/' + testId);
return get(
'/share/api/definition/report/getReport/' + shareId + '/' + testId
);
}
export function getShareInfo(id) {
@ -36,18 +42,18 @@ export function getShareId() {
});
return shareId;
} else {
if (hrefUrl.indexOf("?") > 0) {
let paramArr = hrefUrl.split("?");
if (hrefUrl.indexOf('?') > 0) {
let paramArr = hrefUrl.split('?');
if (paramArr.length > 1) {
let shareId = paramArr[1];
if (shareId.indexOf("#") > 0) {
shareId = shareId.split("#")[0];
if (shareId.indexOf('#') > 0) {
shareId = shareId.split('#')[0];
}
return shareId;
}
}
}
return "";
return '';
}
export function getShareRedirectUrl(data) {

View File

@ -1,5 +1,5 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import { get, post } from 'metersphere-frontend/src/plugins/request';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
export function getPlanStageOption() {
let projectID = getCurrentProjectID();
@ -7,7 +7,7 @@ export function getPlanStageOption() {
}
export function planPage(page, pageSize, params) {
return post('/test/plan/list/all/'+ page + '/' + pageSize, params);
return post('/test/plan/list/all/' + page + '/' + pageSize, params);
}
export function testPlanGetPrincipal(id) {

View File

@ -1,7 +1,6 @@
import {get} from "metersphere-frontend/src/plugins/request"
import { get } from 'metersphere-frontend/src/plugins/request';
export function getTestResourcePools() {
let url = '/testresourcepool/list/quota/valid';
return get(url);
}

View File

@ -1,6 +1,5 @@
import {get} from "metersphere-frontend/src/plugins/request"
import { get } from 'metersphere-frontend/src/plugins/request';
export function getCurrentByResourceId(id) {
return get('/api/current/user/' + id, () => {
});
return get('/api/current/user/' + id, () => {});
}

View File

@ -1,4 +1,4 @@
import {post, get} from "metersphere-frontend/src/plugins/request"
import { post, get } from 'metersphere-frontend/src/plugins/request';
export function apiTestReRun(condition) {
return post('/api/test/exec/rerun', condition);
@ -26,4 +26,4 @@ export function getProjectVersions(projectId) {
export function versionEnableByProjectId(projectId) {
return get('/project/version/enable/' + projectId);
}
}

View File

@ -1,31 +1,27 @@
<template>
<el-col>
<ms-api-header-menus/>
<ms-api-header-menus />
<div>
<transition>
<keep-alive>
<router-view :baseUrl="baseUrl"/>
<router-view :baseUrl="baseUrl" />
</keep-alive>
</transition>
</div>
</el-col>
</template>
<script>
import MsApiHeaderMenus from './head/ApiHeaderMenus';
import MsApiHeaderMenus from "./head/ApiHeaderMenus";
export default {
name: "MsApiTest",
components: {MsApiHeaderMenus},
data() {
return {
baseUrl: "api"
}
}
}
export default {
name: 'MsApiTest',
components: { MsApiHeaderMenus },
data() {
return {
baseUrl: 'api',
};
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -15,14 +15,22 @@
page-source="scenario"
:is-trash-data="trashEnable"
:type="'edit'"
:total='total'
ref="nodeTree"/>
:total="total"
ref="nodeTree"
/>
</ms-aside-container>
<ms-main-container>
<el-tabs v-model="activeName" @tab-click="addTab" @tab-remove="closeConfirm">
<el-tabs
v-model="activeName"
@tab-click="addTab"
@tab-remove="closeConfirm"
>
<el-tab-pane
name="trash" :closable="true"
:label="$t('commons.trash')" v-if="trashEnable">
name="trash"
:closable="true"
:label="$t('commons.trash')"
v-if="trashEnable"
>
<ms-api-scenario-list
@getTrashCase="getTrashCase"
@refreshTree="refreshTree"
@ -39,13 +47,21 @@
:custom-num="customNum"
:init-api-table-opretion="initApiTableOpretion"
@updateInitApiTableOpretion="updateInitApiTableOpretion"
ref="apiTrashScenarioList">
ref="apiTrashScenarioList"
>
<template v-slot:version>
<mx-version-select v-xpack :project-id="projectId" @changeVersion="changeVersion"/>
<mx-version-select
v-xpack
:project-id="projectId"
@changeVersion="changeVersion"
/>
</template>
</ms-api-scenario-list>
</el-tab-pane>
<el-tab-pane name="default" :label="$t('api_test.automation.scenario_list')">
<el-tab-pane
name="default"
:label="$t('api_test.automation.scenario_list')"
>
<ms-api-scenario-list
v-if="!trashEnable"
@getTrashCase="getTrashCase"
@ -63,41 +79,63 @@
:custom-num="customNum"
:init-api-table-opretion="initApiTableOpretion"
@updateInitApiTableOpretion="updateInitApiTableOpretion"
ref="apiScenarioList">
ref="apiScenarioList"
>
<template v-slot:version>
<mx-version-select v-xpack :project-id="projectId" @changeVersion="changeVersion"/>
<mx-version-select
v-xpack
:project-id="projectId"
@changeVersion="changeVersion"
/>
</template>
</ms-api-scenario-list>
</el-tab-pane>
<el-tab-pane
:key="item.name"
v-for="(item) in tabs"
v-for="item in tabs"
:label="item.label"
:name="item.name"
closable>
<el-tooltip slot="label" effect="dark" :content="item.label" placement="bottom-start"
class="ms-tab-name-width">
closable
>
<el-tooltip
slot="label"
effect="dark"
:content="item.label"
placement="bottom-start"
class="ms-tab-name-width"
>
<span>{{ item.label }}</span>
</el-tooltip>
<div class="ms-api-scenario-div">
<ms-edit-api-scenario @refresh="refresh" @openScenario="editScenario" @closePage="closePage"
:currentScenario="item.currentScenario"
:custom-num="customNum" :moduleOptions="moduleOptions" ref="autoScenarioConfig"/>
<ms-edit-api-scenario
@refresh="refresh"
@openScenario="editScenario"
@closePage="closePage"
:currentScenario="item.currentScenario"
:custom-num="customNum"
:moduleOptions="moduleOptions"
ref="autoScenarioConfig"
/>
</div>
</el-tab-pane>
<el-tab-pane name="add" v-if="hasPermission('PROJECT_API_SCENARIO:READ+CREATE')">
<el-tab-pane
name="add"
v-if="hasPermission('PROJECT_API_SCENARIO:READ+CREATE')"
>
<template v-slot:label>
<el-dropdown @command="handleCommand">
<el-button type="primary" plain icon="el-icon-plus" size="mini"/>
<el-button type="primary" plain icon="el-icon-plus" size="mini" />
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="ADD"
v-permission="['PROJECT_API_SCENARIO:READ+CREATE']">
<el-dropdown-item
command="ADD"
v-permission="['PROJECT_API_SCENARIO:READ+CREATE']"
>
{{ $t('api_test.automation.add_scenario') }}
</el-dropdown-item>
<el-dropdown-item command="CLOSE_ALL">{{ $t('api_test.definition.request.close_all_label') }}
<el-dropdown-item command="CLOSE_ALL"
>{{ $t('api_test.definition.request.close_all_label') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -109,29 +147,45 @@
</template>
<script>
import {getCurrentProjectID, getCurrentUser, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {getUUID} from "metersphere-frontend/src/utils";
import {hasPermission} from "metersphere-frontend/src/utils/permission";
import {PROJECT_ID, WORKSPACE_ID} from "metersphere-frontend/src/utils/constants";
import {buildTree} from "metersphere-frontend/src/model/NodeTree";
import {getScenarioById, getScenarioByTrash} from "@/api/scenario";
import {getOwnerProjectIds, getProject, getProjectConfig} from "@/api/project";
import {getModuleByProjectId} from "@/api/scenario-module";
import {useApiStore} from "@/store";
import {
getCurrentProjectID,
getCurrentUser,
getCurrentWorkspaceId,
} from 'metersphere-frontend/src/utils/token';
import { getUUID } from 'metersphere-frontend/src/utils';
import { hasPermission } from 'metersphere-frontend/src/utils/permission';
import {
PROJECT_ID,
WORKSPACE_ID,
} from 'metersphere-frontend/src/utils/constants';
import { buildTree } from 'metersphere-frontend/src/model/NodeTree';
import { getScenarioById, getScenarioByTrash } from '@/api/scenario';
import {
getOwnerProjectIds,
getProject,
getProjectConfig,
} from '@/api/project';
import { getModuleByProjectId } from '@/api/scenario-module';
import { useApiStore } from '@/store';
const store = useApiStore();
const jsondiffpatch = require('jsondiffpatch');
export default {
name: "ApiAutomation",
name: 'ApiAutomation',
components: {
MxVersionSelect: () => import("metersphere-frontend/src/components/version/MxVersionSelect"),
MsApiScenarioModule: () => import("@/business/automation/scenario/ApiScenarioModule"),
MsApiScenarioList: () => import("@/business/automation/scenario/ApiScenarioList"),
MsMainContainer: () => import("metersphere-frontend/src/components/MsMainContainer"),
MsAsideContainer: () => import("metersphere-frontend/src/components/MsAsideContainer"),
MsContainer: () => import("metersphere-frontend/src/components/MsContainer"),
MsEditApiScenario: () => import("./scenario/EditApiScenario")
MxVersionSelect: () =>
import('metersphere-frontend/src/components/version/MxVersionSelect'),
MsApiScenarioModule: () =>
import('@/business/automation/scenario/ApiScenarioModule'),
MsApiScenarioList: () =>
import('@/business/automation/scenario/ApiScenarioList'),
MsMainContainer: () =>
import('metersphere-frontend/src/components/MsMainContainer'),
MsAsideContainer: () =>
import('metersphere-frontend/src/components/MsAsideContainer'),
MsContainer: () =>
import('metersphere-frontend/src/components/MsContainer'),
MsEditApiScenario: () => import('./scenario/EditApiScenario'),
},
comments: {},
computed: {
@ -167,7 +221,7 @@ export default {
trashEnable: false,
selectNodeIds: [],
nodeTree: [],
currentModulePath: "",
currentModulePath: '',
customNum: false,
//API
initApiTableOpretion: 'init',
@ -204,10 +258,11 @@ export default {
this.renderComponent = true;
});
},
'$route'(to, from) { // ctrl s
$route(to, from) {
// ctrl s
if (to.path.indexOf('/api/automation') == -1) {
if (this.$refs && this.$refs.autoScenarioConfig) {
this.$refs.autoScenarioConfig.forEach(item => {
this.$refs.autoScenarioConfig.forEach((item) => {
item.removeListener();
});
}
@ -215,12 +270,13 @@ export default {
},
selectNodeIds() {
if (!this.trashEnable) {
this.activeName = "default";
this.activeName = 'default';
}
},
activeName() {
this.isAsideHidden = (this.activeName === 'default' || this.activeName === 'trash');
}
this.isAsideHidden =
this.activeName === 'default' || this.activeName === 'trash';
},
},
methods: {
hasPermission,
@ -234,20 +290,28 @@ export default {
if (!params) {
return;
}
if (typeof params === "string") {
let paramArr = params.split("edit:");
if (typeof params === 'string') {
let paramArr = params.split('edit:');
if (paramArr.length !== 2) {
return;
}
let scenarioId = paramArr[1];
//
getScenarioById(scenarioId).then(response => {
getScenarioById(scenarioId).then((response) => {
let data = response.data;
if (data) {
let row = data;
let checks = ["array", "object"];
if (row && row.tags && (checks.indexOf(Object.prototype.toString.call(row.tags)
.match(/\[object (\w+)\]/)[1].toLowerCase()) !== -1)) {
let checks = ['array', 'object'];
if (
row &&
row.tags &&
checks.indexOf(
Object.prototype.toString
.call(row.tags)
.match(/\[object (\w+)\]/)[1]
.toLowerCase()
) !== -1
) {
row.tags = JSON.parse(row.tags);
}
//
@ -262,11 +326,14 @@ export default {
}
},
initModules(row, projectId) {
getModuleByProjectId(projectId).then(response => {
getModuleByProjectId(projectId).then((response) => {
if (response.data) {
response.data.forEach(node => {
node.name = node.name === '未规划场景' ? this.$t('api_test.automation.unplanned_scenario') : node.name
buildTree(node, {path: ''});
response.data.forEach((node) => {
node.name =
node.name === '未规划场景'
? this.$t('api_test.automation.unplanned_scenario')
: node.name;
buildTree(node, { path: '' });
});
this.moduleOptions = response.data;
}
@ -276,13 +343,13 @@ export default {
changeRedirectParam(redirectIDParam) {
this.redirectID = redirectIDParam;
if (redirectIDParam != null) {
if (this.redirectFlag == "none") {
this.activeName = "default";
if (this.redirectFlag == 'none') {
this.activeName = 'default';
this.addListener();
this.redirectFlag = "redirected";
this.redirectFlag = 'redirected';
}
} else {
this.redirectFlag = "none";
this.redirectFlag = 'none';
}
},
getPath(id, arr) {
@ -290,7 +357,7 @@ export default {
return null;
}
if (arr) {
arr.forEach(item => {
arr.forEach((item) => {
if (item.id === id) {
this.currentModulePath = item.path;
}
@ -311,16 +378,19 @@ export default {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
this.currentModulePath = "";
this.currentModulePath = '';
if (tab.name === 'add') {
let label = this.$t('api_test.automation.add_scenario');
let name = getUUID().substring(0, 8);
this.activeName = name;
let currentScenario = {
status: "Underway", principal: getCurrentUser().id,
apiScenarioModuleId: "default-module", id: getUUID(),
modulePath: "/" + this.$t("commons.module_title"),
level: "P0", type: "add"
status: 'Underway',
principal: getCurrentUser().id,
apiScenarioModuleId: 'default-module',
id: getUUID(),
modulePath: '/' + this.$t('commons.module_title'),
level: 'P0',
type: 'add',
};
if (this.nodeTree && this.nodeTree.length > 0) {
currentScenario.apiScenarioModuleId = this.nodeTree[0].id;
@ -332,7 +402,11 @@ export default {
this.getPath(this.selectNodeIds[0], this.moduleOptions);
currentScenario.modulePath = this.currentModulePath;
}
this.tabs.push({label: label, name: name, currentScenario: currentScenario});
this.tabs.push({
label: label,
name: name,
currentScenario: currentScenario,
});
}
if (tab.name === 'edit') {
let label = this.$t('api_test.automation.add_scenario');
@ -340,55 +414,68 @@ export default {
this.activeName = name;
label = tab.currentScenario.name;
if (!tab.currentScenario.level) {
tab.currentScenario.level = "P0";
tab.currentScenario.level = 'P0';
}
this.tabs.push({label: label, name: name, currentScenario: tab.currentScenario});
this.tabs.push({
label: label,
name: name,
currentScenario: tab.currentScenario,
});
}
if (this.$refs && this.$refs.autoScenarioConfig) {
this.$refs.autoScenarioConfig.forEach(item => {
this.$refs.autoScenarioConfig.forEach((item) => {
item.removeListener();
}); // tab ctrl + s
}); // tab ctrl + s
this.addListener();
}
},
addListener() {
let index = this.tabs.findIndex(item => item.name === this.activeName); // tabindex
if (index != -1) { // tab
let index = this.tabs.findIndex((item) => item.name === this.activeName); // tabindex
if (index != -1) {
// tab
this.$nextTick(() => {
this.$refs.autoScenarioConfig[index].addListener();
});
}
},
handleTabClose() {
let message = "";
this.tabs.forEach(t => {
let message = '';
this.tabs.forEach((t) => {
this.diff(t);
if (t && this.isSave) {
message += t.currentScenario.name + "";
message += t.currentScenario.name + '';
this.isSave = false;
}
});
if (message !== "") {
this.$alert(this.$t('commons.scenario') + " [ " + message.substr(0, message.length - 1) + " ] " + this.$t('commons.confirm_info'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
callback: (action) => {
if (action === 'confirm') {
this.tabs = [];
this.trashEnable = false;
this.activeName = "default";
this.isSave = false;
// vuex
store.scenarioEnvMap = new Map();
} else {
this.isSave = false;
}
if (message !== '') {
this.$alert(
this.$t('commons.scenario') +
' [ ' +
message.substr(0, message.length - 1) +
' ] ' +
this.$t('commons.confirm_info'),
'',
{
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
callback: (action) => {
if (action === 'confirm') {
this.tabs = [];
this.trashEnable = false;
this.activeName = 'default';
this.isSave = false;
// vuex
store.scenarioEnvMap = new Map();
} else {
this.isSave = false;
}
},
}
});
);
} else {
this.tabs = [];
this.trashEnable = false;
this.activeName = "default";
this.activeName = 'default';
this.refresh();
this.isSave = false;
}
@ -398,28 +485,28 @@ export default {
},
handleCommand(e) {
switch (e) {
case "ADD":
this.addTab({name: 'add'});
case 'ADD':
this.addTab({ name: 'add' });
break;
case "CLOSE_ALL":
case 'CLOSE_ALL':
this.handleTabClose();
break;
default:
this.addTab({name: 'add'});
this.addTab({ name: 'add' });
break;
}
},
closePage(targetName) {
this.tabs = this.tabs.filter(tab => tab.label !== targetName);
this.tabs = this.tabs.filter((tab) => tab.label !== targetName);
if (this.tabs.length > 0) {
this.activeName = this.tabs[this.tabs.length - 1].name;
this.addListener(); //
} else {
this.activeName = "default";
this.activeName = 'default';
}
},
diff(t) {
if (t.currentScenario.type !== "add") {
if (t.currentScenario.type !== 'add') {
let v1 = t.currentScenario.scenarioDefinitionOrg;
let v2 = {
apiScenarioModuleId: t.currentScenario.apiScenarioModuleId,
@ -429,7 +516,7 @@ export default {
level: t.currentScenario.level,
tags: t.currentScenario.tags,
description: t.currentScenario.description,
scenarioDefinition: t.currentScenario.scenarioDefinition
scenarioDefinition: t.currentScenario.scenarioDefinition,
};
let v3 = JSON.parse(JSON.stringify(v2));
if (v1.scenarioDefinition) {
@ -438,7 +525,10 @@ export default {
if (v3.scenarioDefinition) {
this.deleteResourceIds(v3.scenarioDefinition);
}
let delta = jsondiffpatch.diff(JSON.parse(JSON.stringify(v1)), JSON.parse(JSON.stringify(v3)));
let delta = jsondiffpatch.diff(
JSON.parse(JSON.stringify(v1)),
JSON.parse(JSON.stringify(v3))
);
if (delta) {
this.isSave = true;
}
@ -452,7 +542,7 @@ export default {
},
deleteResourceIds(array) {
if (array instanceof Array && array.length > 0) {
array.forEach(item => {
array.forEach((item) => {
if (item.currentScenarioId && item.currentScenarioId.length > 0) {
delete item.currentScenarioId;
}
@ -490,7 +580,7 @@ export default {
delete item.maxThreads;
}
if (item.parentIndex) {
delete item.parentIndex
delete item.parentIndex;
}
if (item.connectTimeout) {
delete item.connectTimeout;
@ -520,20 +610,24 @@ export default {
item.delay = Number(item.delay);
}
if (item.body && item.body.kvs) {
item.body.kvs.forEach(v => {
item.body.kvs.forEach((v) => {
if (v.files) {
delete v.files;
}
})
});
}
if (item.body && ((item.body.binary && item.body.binary.length === 0) || (item.body.kvs && item.body.kvs.length === 0))) {
if (
item.body &&
((item.body.binary && item.body.binary.length === 0) ||
(item.body.kvs && item.body.kvs.length === 0))
) {
delete item.body;
}
delete item.projectId;
if (item.hashTree && item.hashTree.length > 0) {
this.deleteResourceIds(item.hashTree);
}
})
});
}
},
closeConfirm(targetName) {
@ -541,28 +635,38 @@ export default {
this.selectNodeIds = [];
this.trashEnable = false;
} else {
let message = "";
this.tabs.forEach(tab => {
let message = '';
this.tabs.forEach((tab) => {
if (tab.name === targetName) {
this.diff(tab);
if (tab && this.isSave) {
message += tab.currentScenario.name ? tab.currentScenario.name : this.$t('api_test.automation.add_scenario');
message += tab.currentScenario.name
? tab.currentScenario.name
: this.$t('api_test.automation.add_scenario');
}
}
})
if (message !== "") {
this.$alert(this.$t('commons.scenario') + " [ " + message + " ] " + this.$t('commons.confirm_info'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
callback: (action) => {
if (action === 'confirm') {
this.removeTab(targetName);
this.isSave = false;
} else {
this.isSave = false;
}
});
if (message !== '') {
this.$alert(
this.$t('commons.scenario') +
' [ ' +
message +
' ] ' +
this.$t('commons.confirm_info'),
'',
{
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
callback: (action) => {
if (action === 'confirm') {
this.removeTab(targetName);
this.isSave = false;
} else {
this.isSave = false;
}
},
}
});
);
} else {
this.isSave = false;
this.removeTab(targetName);
@ -573,28 +677,33 @@ export default {
}
},
removeTab(targetName) {
let index = this.tabs.findIndex(item => item.name === targetName);
let index = this.tabs.findIndex((item) => item.name === targetName);
if (index !== -1) {
// vuex
let tab = this.tabs[index];
if (tab && tab.currentScenario && store.scenarioEnvMap && store.scenarioEnvMap instanceof Map) {
if (
tab &&
tab.currentScenario &&
store.scenarioEnvMap &&
store.scenarioEnvMap instanceof Map
) {
store.scenarioEnvMap.forEach((v, k) => {
if (k.indexOf(tab.currentScenario.id) !== -1) {
store.scenarioEnvMap.delete(k);
}
})
});
}
this.tabs.splice(index, 1);
if (tab) {
tab = undefined;
}
}
this.tabs = this.tabs.filter(tab => tab.name !== targetName);
this.tabs = this.tabs.filter((tab) => tab.name !== targetName);
if (this.tabs.length > 0) {
this.activeName = this.tabs[this.tabs.length - 1].name;
this.addListener(); //
} else {
this.activeName = "default";
this.activeName = 'default';
}
},
setTabLabel(data) {
@ -655,15 +764,18 @@ export default {
this.$error(this.$t('api_test.scenario_jump_message'));
return;
}
const index = this.tabs.find(p => p.currentScenario.id === row.id && p.currentScenario.copy === row.copy);
const index = this.tabs.find(
(p) =>
p.currentScenario.id === row.id && p.currentScenario.copy === row.copy
);
if (!index) {
this.addTab({name: 'edit', currentScenario: row});
this.addTab({ name: 'edit', currentScenario: row });
} else {
this.activeName = index.name;
}
},
nodeChange(node, nodeIds, pNodes) {
this.initApiTableOpretion = "nodeChange";
this.initApiTableOpretion = 'nodeChange';
this.selectNodeIds = nodeIds;
},
setModuleOptions(data) {
@ -676,29 +788,31 @@ export default {
this.$route.params.dataSelectRange = 'all';
},
enableTrash(data) {
this.activeName = "default";
this.initApiTableOpretion = "enableTrash";
this.activeName = 'default';
this.initApiTableOpretion = 'enableTrash';
this.trashEnable = data;
if (data) {
this.activeName = "trash";
this.activeName = 'trash';
} else {
this.activeName = "default";
this.activeName = 'default';
}
this.getTrashCase();
},
getTrashCase() {
let param = {};
param.projectId = this.projectId;
getScenarioByTrash(param).then(response => {
getScenarioByTrash(param).then((response) => {
this.total = response.data;
});
},
getProject() {
getProjectConfig(this.projectId, '/SCENARIO_CUSTOM_NUM').then(result => {
if (result.data) {
this.customNum = result.data.scenarioCustomNum;
getProjectConfig(this.projectId, '/SCENARIO_CUSTOM_NUM').then(
(result) => {
if (result.data) {
this.customNum = result.data.scenarioCustomNum;
}
}
});
);
},
updateInitApiTableOpretion(param) {
this.initApiTableOpretion = param;
@ -708,16 +822,17 @@ export default {
this.$refs.apiScenarioList.condition.versionId = currentVersion || null;
}
if (this.$refs.apiTrashScenarioList) {
this.$refs.apiTrashScenarioList.condition.versionId = currentVersion || null;
this.$refs.apiTrashScenarioList.condition.versionId =
currentVersion || null;
}
this.refreshAll();
},
clickResource(resource) {
let workspaceId = getCurrentWorkspaceId();
let isTurnSpace = true
let isTurnSpace = true;
if (resource.projectId !== getCurrentProjectID()) {
isTurnSpace = false;
getProject(resource.projectId).then(response => {
getProject(resource.projectId).then((response) => {
if (response.data) {
workspaceId = response.data.workspaceId;
isTurnSpace = true;
@ -733,27 +848,27 @@ export default {
name: 'ApiAutomationWithQuery',
params: {
redirectID: getUUID(),
dataType: "scenario",
dataType: 'scenario',
dataSelectRange: 'edit:' + resource.id,
projectId: resource.projectId,
workspaceId: workspaceId
}
workspaceId: workspaceId,
},
});
if (isTurnSpace) {
window.open(automationData.href, '_blank');
}
},
checkPermission(resource, workspaceId, isTurnSpace) {
getOwnerProjectIds().then(res => {
const project = res.data.find(p => p === resource.projectId);
getOwnerProjectIds().then((res) => {
const project = res.data.find((p) => p === resource.projectId);
if (!project) {
this.$warning(this.$t('commons.no_permission'));
} else {
this.gotoTurn(resource, workspaceId, isTurnSpace)
this.gotoTurn(resource, workspaceId, isTurnSpace);
}
})
}
}
});
},
},
};
</script>

View File

@ -1,16 +1,16 @@
import {editScenario} from "@/api/scenario";
import {getUUID} from "metersphere-frontend/src/utils";
import {ELEMENT_TYPE} from "@/business/automation/scenario/Setting";
import {baseSocket, getUploadConfig} from "@/api/base-network";
import {useApiStore} from "@/store";
import { editScenario } from '@/api/scenario';
import { getUUID } from 'metersphere-frontend/src/utils';
import { ELEMENT_TYPE } from '@/business/automation/scenario/Setting';
import { baseSocket, getUploadConfig } from '@/api/base-network';
import { useApiStore } from '@/store';
const store = useApiStore();
function buildBodyFile(item, bodyUploadFiles, obj, bodyParam) {
if (bodyParam) {
bodyParam.forEach(param => {
bodyParam.forEach((param) => {
if (param.files) {
param.files.forEach(fileItem => {
param.files.forEach((fileItem) => {
if (fileItem.file) {
fileItem.name = fileItem.file.name;
obj.bodyFileRequestIds.push(item.id);
@ -31,9 +31,9 @@ function setFiles(item, bodyUploadFiles, obj) {
function recursiveFile(arr, bodyUploadFiles, obj) {
if (arr) {
arr.forEach(item => {
arr.forEach((item) => {
setFiles(item, bodyUploadFiles, obj);
if (item.hashTree && item.hashTree.length > 0) {
if (item.hashTree && item.hashTree.length > 0) {
recursiveFile(item.hashTree, bodyUploadFiles, obj);
}
});
@ -43,12 +43,12 @@ function recursiveFile(arr, bodyUploadFiles, obj) {
export function getBodyUploadFiles(obj, scenarioDefinition) {
let bodyUploadFiles = [];
obj.bodyFileRequestIds = [];
scenarioDefinition.forEach(item => {
scenarioDefinition.forEach((item) => {
setFiles(item, bodyUploadFiles, obj);
if (item.hashTree && item.hashTree.length > 0) {
if (item.hashTree && item.hashTree.length > 0) {
recursiveFile(item.hashTree, bodyUploadFiles, obj);
}
})
});
return bodyUploadFiles;
}
@ -57,9 +57,9 @@ function getScenarioFiles(obj) {
obj.scenarioFileIds = [];
// 场景变量csv 文件
if (obj.variables) {
obj.variables.forEach(param => {
obj.variables.forEach((param) => {
if (param.type === 'CSV' && param.files) {
param.files.forEach(item => {
param.files.forEach((item) => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
@ -69,17 +69,23 @@ function getScenarioFiles(obj) {
obj.scenarioFileIds.push(item.id);
scenarioFiles.push(item.file);
}
})
});
}
});
}
return scenarioFiles;
}
export function saveScenario(url, scenario, scenarioDefinition, _this, success) {
export function saveScenario(
url,
scenario,
scenarioDefinition,
_this,
success
) {
let bodyFiles = getBodyUploadFiles(scenario, scenarioDefinition);
if (store.pluginFiles && store.pluginFiles.length > 0) {
store.pluginFiles.forEach(fileItem => {
store.pluginFiles.forEach((fileItem) => {
if (fileItem.file) {
scenario.bodyFileRequestIds.push(fileItem.file.uid);
bodyFiles.push(fileItem.file);
@ -89,25 +95,31 @@ export function saveScenario(url, scenario, scenarioDefinition, _this, success)
let scenarioFiles = getScenarioFiles(scenario);
let formData = new FormData();
if (bodyFiles) {
bodyFiles.forEach(f => {
formData.append("bodyFiles", f);
})
bodyFiles.forEach((f) => {
formData.append('bodyFiles', f);
});
}
if (scenarioFiles) {
scenarioFiles.forEach(f => {
formData.append("scenarioFiles", f);
})
scenarioFiles.forEach((f) => {
formData.append('scenarioFiles', f);
});
}
formData.append('request', new Blob([JSON.stringify(scenario)], {type: "application/json"}));
formData.append(
'request',
new Blob([JSON.stringify(scenario)], { type: 'application/json' })
);
let config = getUploadConfig(url, formData);
editScenario(config).then((response) => {
if (success) {
success(response.data);
editScenario(config).then(
(response) => {
if (success) {
success(response.data);
}
},
(error) => {
_this.isPreventReClick = false;
_this.errorRefresh();
}
}, error => {
_this.isPreventReClick = false;
_this.errorRefresh();
})
);
}
export function savePreciseEnvProjectIds(projectIds, envMap) {
@ -120,19 +132,22 @@ export function savePreciseEnvProjectIds(projectIds, envMap) {
}
for (let id of projectIds) {
if (!envMap.get(id)) {
envMap.set(id, "");
envMap.set(id, '');
}
}
}
}
export function scenarioSort(_this) {
for (let i in _this.scenarioDefinition) {
// 排序
_this.$set(_this.scenarioDefinition[i], 'index', Number(i) + 1);
// 设置循环控制
if (_this.scenarioDefinition[i].type === ELEMENT_TYPE.LoopController && _this.scenarioDefinition[i].hashTree && _this.scenarioDefinition[i].hashTree.length > 1) {
if (
_this.scenarioDefinition[i].type === ELEMENT_TYPE.LoopController &&
_this.scenarioDefinition[i].hashTree &&
_this.scenarioDefinition[i].hashTree.length > 1
) {
_this.scenarioDefinition[i].countController.proceed = true;
}
// 设置项目ID
@ -140,15 +155,32 @@ export function scenarioSort(_this) {
_this.scenarioDefinition[i].projectId = _this.projectId;
}
if (_this.scenarioDefinition[i].hashTree != undefined && _this.scenarioDefinition[i].hashTree.length > 0) {
if (
_this.scenarioDefinition[i].hashTree != undefined &&
_this.scenarioDefinition[i].hashTree.length > 0
) {
if (_this.hideTreeNode) {
_this.hideTreeNode(_this.scenarioDefinition[i], _this.scenarioDefinition[i].hashTree);
_this.hideTreeNode(
_this.scenarioDefinition[i],
_this.scenarioDefinition[i].hashTree
);
}
recursiveSorting(_this, _this.scenarioDefinition[i].hashTree, _this.scenarioDefinition[i].projectId);
recursiveSorting(
_this,
_this.scenarioDefinition[i].hashTree,
_this.scenarioDefinition[i].projectId
);
}
// 添加debug结果
if (_this.debugResult && _this.debugResult.get(_this.scenarioDefinition[i].id + _this.scenarioDefinition[i].name)) {
_this.scenarioDefinition[i].requestResult = _this.debugResult.get(_this.scenarioDefinition[i].id + _this.scenarioDefinition[i].name);
if (
_this.debugResult &&
_this.debugResult.get(
_this.scenarioDefinition[i].id + _this.scenarioDefinition[i].name
)
) {
_this.scenarioDefinition[i].requestResult = _this.debugResult.get(
_this.scenarioDefinition[i].id + _this.scenarioDefinition[i].name
);
}
}
}
@ -156,11 +188,18 @@ export function scenarioSort(_this) {
export function recursiveSorting(_this, arr, scenarioProjectId) {
for (let i in arr) {
arr[i].index = Number(i) + 1;
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].loopType === "LOOP_COUNT" && arr[i].hashTree && arr[i].hashTree.length > 1) {
if (
arr[i].type === ELEMENT_TYPE.LoopController &&
arr[i].loopType === 'LOOP_COUNT' &&
arr[i].hashTree &&
arr[i].hashTree.length > 1
) {
arr[i].countController.proceed = true;
}
if (!arr[i].projectId) {
arr[i].projectId = scenarioProjectId ? scenarioProjectId : _this.projectId;
arr[i].projectId = scenarioProjectId
? scenarioProjectId
: _this.projectId;
}
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
if (_this.hideTreeNode) {
@ -175,12 +214,11 @@ export function recursiveSorting(_this, arr, scenarioProjectId) {
}
}
export function copyScenarioRow(row, node) {
if (!row || !node) {
return;
}
const parent = node.parent
const parent = node.parent;
const hashTree = parent.data.hashTree || parent.data;
// 深度复制
let obj = JSON.parse(JSON.stringify(row));
@ -191,7 +229,7 @@ export function copyScenarioRow(row, node) {
if (obj.name) {
obj.name = obj.name + '_copy';
}
const index = hashTree.findIndex(d => d.resourceId === row.resourceId);
const index = hashTree.findIndex((d) => d.resourceId === row.resourceId);
if (index != -1) {
hashTree.splice(index + 1, 0, obj);
} else {
@ -201,12 +239,12 @@ export function copyScenarioRow(row, node) {
}
export function resetResourceId(hashTree) {
hashTree.forEach(item => {
hashTree.forEach((item) => {
item.resourceId = getUUID();
if (item.hashTree && item.hashTree.length > 0) {
resetResourceId(item.hashTree);
}
})
});
}
export function getReportMessageSocket(reportId) {

View File

@ -14,14 +14,23 @@
:report="report"
:project-env-map="projectEnvMap"
@reportExport="handleExport"
@reportSave="handleSave"/>
@reportSave="handleSave"
/>
<!-- content -->
<main v-if="isNotRunning">
<!-- content header chart -->
<ms-metric-chart :content="content" :totalTime="totalTime" :report="report"/>
<ms-metric-chart
:content="content"
:totalTime="totalTime"
:report="report"
/>
<el-tabs v-model="activeName" @tab-click="handleClick" style="min-width: 1200px">
<el-tabs
v-model="activeName"
@tab-click="handleClick"
style="min-width: 1200px"
>
<!-- all step-->
<el-tab-pane label="All" name="total">
<ms-scenario-results
@ -31,28 +40,27 @@
:is-share="isShare"
:share-id="shareId"
v-on:requestResult="requestResult"
ref="resultsTree"/>
ref="resultsTree"
/>
</el-tab-pane>
<!-- fail step -->
<el-tab-pane name="fail">
<template slot="label">
Error
</template>
<template slot="label"> Error </template>
<ms-scenario-results
v-on:requestResult="requestResult"
:console="content.console"
:report="report"
:is-share="isShare"
:share-id="shareId"
:treeData="fullTreeNodes" ref="failsTree"
:errorReport="content.error"/>
:treeData="fullTreeNodes"
ref="failsTree"
:errorReport="content.error"
/>
</el-tab-pane>
<!--error step -->
<el-tab-pane name="errorReport" v-if="content.errorCode > 0">
<template slot="label">
<span class="fail" style="color: #F6972A">
FakeError
</span>
<span class="fail" style="color: #f6972a"> FakeError </span>
</template>
<ms-scenario-results
v-on:requestResult="requestResult"
@ -60,15 +68,14 @@
:is-share="isShare"
:share-id="shareId"
:console="content.console"
:treeData="fullTreeNodes" ref="errorReportTree"/>
:treeData="fullTreeNodes"
ref="errorReportTree"
/>
</el-tab-pane>
<!-- Not performed step -->
<el-tab-pane name="unExecute" v-if="content.unExecute > 0">
<template slot="label">
<span class="fail"
style="color: #9C9B9A">
Pending
</span>
<span class="fail" style="color: #9c9b9a"> Pending </span>
</template>
<ms-scenario-results
v-on:requestResult="requestResult"
@ -76,7 +83,9 @@
:is-share="isShare"
:share-id="shareId"
:console="content.console"
:treeData="fullTreeNodes" ref="unExecuteTree"/>
:treeData="fullTreeNodes"
ref="unExecuteTree"
/>
</el-tab-pane>
<!-- console -->
<el-tab-pane name="console">
@ -87,14 +96,14 @@
:mode="'text'"
:read-only="true"
:data.sync="content.console"
height="calc(100vh - 500px)"/>
height="calc(100vh - 500px)"
/>
</el-tab-pane>
</el-tabs>
</main>
</section>
</el-card>
</ms-main-container>
</ms-container>
<!--export report-->
@ -105,36 +114,36 @@
:title="report.name"
:content="content"
:report="report"
:total-time="totalTime"/>
:total-time="totalTime"
/>
</div>
</template>
<script>
import MsRequestResult from "./components/RequestResult";
import MsRequestResultTail from "./components/RequestResultTail";
import MsScenarioResult from "./components/ScenarioResult";
import MsMetricChart from "./components/MetricChart";
import MsScenarioResults from "./components/ScenarioResults";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import MsApiReportExport from "./ApiReportExport";
import MsApiReportViewHeader from "./ApiReportViewHeader";
import {RequestFactory} from "../../definition/model/ApiTestModel";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {downloadPDF, getUUID} from "metersphere-frontend/src/utils";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import MsRequestResult from './components/RequestResult';
import MsRequestResultTail from './components/RequestResultTail';
import MsScenarioResult from './components/ScenarioResult';
import MsMetricChart from './components/MetricChart';
import MsScenarioResults from './components/ScenarioResults';
import MsContainer from 'metersphere-frontend/src/components/MsContainer';
import MsMainContainer from 'metersphere-frontend/src/components/MsMainContainer';
import MsApiReportExport from './ApiReportExport';
import MsApiReportViewHeader from './ApiReportViewHeader';
import { RequestFactory } from '../../definition/model/ApiTestModel';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { downloadPDF, getUUID } from 'metersphere-frontend/src/utils';
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
import {
getScenarioReport,
getScenarioReportDetail,
getShareScenarioReport,
reportReName
} from "../../../api/scenario-report";
import {STEP} from "../../automation/scenario/Setting";
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
reportReName,
} from '../../../api/scenario-report';
import { STEP } from '../../automation/scenario/Setting';
import MsCodeEdit from 'metersphere-frontend/src/components/MsCodeEdit';
export default {
name: "MsApiReport",
name: 'MsApiReport',
components: {
MsApiReportViewHeader,
MsApiReportExport,
@ -145,11 +154,11 @@ export default {
MsRequestResultTail,
MsMetricChart,
MsScenarioResult,
MsRequestResult
MsRequestResult,
},
data() {
return {
activeName: "total",
activeName: 'total',
content: {},
report: {},
loading: true,
@ -164,12 +173,12 @@ export default {
requestType: undefined,
fullTreeNodes: [],
showRerunButton: false,
stepFilter: new STEP,
stepFilter: new STEP(),
exportReportIsOk: false,
tempResult: [],
projectEnvMap: {},
showCancel: false
}
showCancel: false,
};
},
activated() {
this.isRequestResult = false;
@ -186,8 +195,8 @@ export default {
isPlan: Boolean,
showCancelButton: {
type: Boolean,
default: false
}
default: false,
},
},
watch: {
reportId() {
@ -199,16 +208,16 @@ export default {
if (this.isTemplate) {
this.getReport();
}
}
},
},
methods: {
filter(index) {
if (index === "1") {
if (index === '1') {
this.$refs.failsTree.filter(index);
} else if (this.activeName === "errorReport") {
this.$refs.errorReportTree.filter("FAKE_ERROR");
} else if (this.activeName === "unExecute") {
this.$refs.unExecuteTree.filter("PENDING");
} else if (this.activeName === 'errorReport') {
this.$refs.errorReportTree.filter('FAKE_ERROR');
} else if (this.activeName === 'unExecute') {
this.$refs.unExecuteTree.filter('PENDING');
}
},
init() {
@ -220,30 +229,46 @@ export default {
this.fullTreeNodes = [];
this.failsTreeNodes = [];
this.isRequestResult = false;
this.activeName = "total";
this.activeName = 'total';
this.showRerunButton = false;
if (this.$route && this.$route.path.startsWith('/api/automation/report')
&& this.$route.query && this.$route.query.list) {
if (
this.$route &&
this.$route.path.startsWith('/api/automation/report') &&
this.$route.query &&
this.$route.query.list
) {
this.$nextTick(() => {
this.showCancel = true;
});
}
},
rerunVerify() {
if (hasLicense() && this.fullTreeNodes && this.fullTreeNodes.length > 0 && !this.isShare) {
this.fullTreeNodes.forEach(item => {
item.redirect = true;
if (item.totalStatus === 'FAIL' || item.totalStatus === 'ERROR' || item.unExecuteTotal > 0
|| (item.type === "API" && item.totalStatus === 'PENDING')) {
this.showRerunButton = true;
}
if (
hasLicense() &&
this.fullTreeNodes &&
this.fullTreeNodes.length > 0 &&
!this.isShare
) {
this.fullTreeNodes.forEach((item) => {
item.redirect = true;
if (
item.totalStatus === 'FAIL' ||
item.totalStatus === 'ERROR' ||
item.unExecuteTotal > 0 ||
(item.type === 'API' && item.totalStatus === 'PENDING')
) {
this.showRerunButton = true;
}
)
});
}
},
handleClick(tab, event) {
this.isRequestResult = false;
if (this.report && this.report.reportVersion && this.report.reportVersion > 1) {
if (
this.report &&
this.report.reportVersion &&
this.report.reportVersion > 1
) {
this.filter(tab.index);
}
},
@ -251,19 +276,19 @@ export default {
this.isActive = !this.isActive;
},
formatResult(res) {
let resMap = new Map;
let resMap = new Map();
let array = [];
if (res && res.scenarios) {
res.scenarios.forEach(item => {
res.scenarios.forEach((item) => {
if (item && item.requestResults) {
item.requestResults.forEach(req => {
item.requestResults.forEach((req) => {
req.responseResult.console = res.console;
resMap.set(req.id + req.name, req);
req.name = item.name + "^@~@^" + req.name + "UUID=" + getUUID();
req.name = item.name + '^@~@^' + req.name + 'UUID=' + getUUID();
array.push(req);
})
});
}
})
});
}
this.formatTree(array, this.fullTreeNodes);
this.sort(this.fullTreeNodes);
@ -276,12 +301,12 @@ export default {
let children = tree;
//1
//hashTreeID
let scenarioId = "";
let scenarioName = "";
let scenarioId = '';
let scenarioName = '';
if (item.scenario) {
let scenarioArr = JSON.parse(item.scenario);
if (scenarioArr.length > 1) {
let scenarioIdArr = scenarioArr[0].split("_");
let scenarioIdArr = scenarioArr[0].split('_');
scenarioId = scenarioIdArr[0];
scenarioName = scenarioIdArr[1];
}
@ -295,14 +320,16 @@ export default {
label: nodeArray[i],
value: item,
};
if (i !== (nodeArray.length - 1)) {
if (i !== nodeArray.length - 1) {
node.children = [];
} else {
if (item.subRequestResults && item.subRequestResults.length > 0) {
let itemChildren = this.deepFormatTreeNode(item.subRequestResults);
let itemChildren = this.deepFormatTreeNode(
item.subRequestResults
);
node.children = itemChildren;
if (node.label.indexOf("UUID=")) {
node.label = node.label.split("UUID=")[0];
if (node.label.indexOf('UUID=')) {
node.label = node.label.split('UUID=')[0];
}
}
}
@ -313,25 +340,24 @@ export default {
let isExist = false;
for (let j in children) {
if (children[j].label === node.label) {
let idIsPath = true;
//ID
//
if (i === nodeArray.length - 2) {
idIsPath = false;
let childId = "";
let childName = "";
let childId = '';
let childName = '';
if (children[j].value && children[j].value.scenario) {
let scenarioArr = JSON.parse(children[j].value.scenario);
if (scenarioArr.length > 1) {
let childArr = scenarioArr[0].split("_");
let childArr = scenarioArr[0].split('_');
childId = childArr[0];
if (childArr.length > 1) {
childName = childArr[1];
}
}
}
if (scenarioId === "") {
if (scenarioId === '') {
idIsPath = true;
} else if (scenarioId === childId) {
idIsPath = true;
@ -344,7 +370,8 @@ export default {
if (i !== nodeArray.length - 1 && !children[j].children) {
children[j].children = [];
}
children = (i === nodeArray.length - 1 ? children : children[j].children);
children =
i === nodeArray.length - 1 ? children : children[j].children;
isExist = true;
break;
}
@ -352,13 +379,19 @@ export default {
}
if (!isExist) {
children.push(node);
if (i !== nodeArray.length - 1 && !children[children.length - 1].children) {
if (
i !== nodeArray.length - 1 &&
!children[children.length - 1].children
) {
children[children.length - 1].children = [];
}
children = (i === nodeArray.length - 1 ? children : children[children.length - 1].children);
children =
i === nodeArray.length - 1
? children
: children[children.length - 1].children;
}
}
})
});
},
deepFormatTreeNode(array) {
@ -369,12 +402,12 @@ export default {
let nodeArray = key.split('<->');
//1
//hashTreeID
let scenarioId = "";
let scenarioName = "";
let scenarioId = '';
let scenarioName = '';
if (item.scenario) {
let scenarioArr = JSON.parse(item.scenario);
if (scenarioArr.length > 1) {
let scenarioIdArr = scenarioArr[0].split("_");
let scenarioIdArr = scenarioArr[0].split('_');
scenarioId = scenarioIdArr[0];
scenarioName = scenarioIdArr[1];
}
@ -383,17 +416,16 @@ export default {
let node = {
label: nodeArray[0],
value: item,
children: []
children: [],
};
if (item.subRequestResults && item.subRequestResults.length > 0) {
let itemChildren = this.deepFormatTreeNode(item.subRequestResults);
node.children = itemChildren;
}
children.push(node);
children.forEach(itemNode => {
children.forEach((itemNode) => {
returnChildren.push(itemNode);
});
});
return returnChildren;
},
@ -412,7 +444,10 @@ export default {
//
if (scenarioDefinition[i]) {
scenarioDefinition[i].index = Number(i) + 1;
if (scenarioDefinition[i].children && scenarioDefinition[i].children.length > 0) {
if (
scenarioDefinition[i].children &&
scenarioDefinition[i].children.length > 0
) {
this.recursiveSorting(scenarioDefinition[i].children);
}
}
@ -433,12 +468,13 @@ export default {
this.fullTreeNodes = report.steps;
this.content.console = report.console;
this.content.error = report.error;
let successCount = (report.total - report.error - report.errorCode - report.unExecute);
let successCount =
report.total - report.error - report.errorCode - report.unExecute;
this.content.success = successCount;
this.totalTime = report.totalTime;
}
this.exportReportIsOk = true;
setTimeout(this.startExport, 500)
setTimeout(this.startExport, 500);
});
}
},
@ -454,7 +490,7 @@ export default {
}
} else if (this.isShare) {
if (this.reportId) {
getShareScenarioReport(this.shareId, this.reportId).then(res => {
getShareScenarioReport(this.shareId, this.reportId).then((res) => {
let data = res.data;
if (data) {
this.checkReport(data);
@ -463,7 +499,7 @@ export default {
});
}
} else if (this.reportId) {
getScenarioReport(this.reportId).then(res => {
getScenarioReport(this.reportId).then((res) => {
let data = res.data;
this.checkReport(data);
this.handleGetScenarioReport(data);
@ -482,7 +518,7 @@ export default {
if (this.report.reportVersion && this.report.reportVersion > 1) {
this.report.status = data.status;
if (!this.isNotRunning) {
setTimeout(this.getReport, 2000)
setTimeout(this.getReport, 2000);
} else {
if (data.content) {
let report = JSON.parse(data.content);
@ -493,12 +529,20 @@ export default {
this.fullTreeNodes = report.steps;
this.content.console = report.console;
this.content.error = report.error;
let successCount = (report.total - report.error - report.errorCode - report.unExecute);
let successCount =
report.total -
report.error -
report.errorCode -
report.unExecute;
this.content.success = successCount;
this.totalTime = report.totalTime;
}
//
if (this.report && this.report.reportType === 'SCENARIO_INTEGRATED' || this.report.reportType === 'API_INTEGRATED') {
if (
(this.report &&
this.report.reportType === 'SCENARIO_INTEGRATED') ||
this.report.reportType === 'API_INTEGRATED'
) {
this.rerunVerify();
}
this.loading = false;
@ -517,39 +561,45 @@ export default {
}
if (Array.isArray(origin)) {
this.sortChildren(origin);
origin.forEach(v => {
origin.forEach((v) => {
if (v.children) {
this.checkOrder(v.children)
this.checkOrder(v.children);
}
})
});
}
},
sortChildren(source) {
if (!source) {
return;
}
source.forEach(item => {
source.forEach((item) => {
let children = item.children;
if (children && children.length > 0) {
let tempArr = new Array(children.length);
let tempMap = new Map();
for (let i = 0; i < children.length; i++) {
if (!children[i].value || !children[i].value.startTime || children[i].value.startTime === 0) {
if (
!children[i].value ||
!children[i].value.startTime ||
children[i].value.startTime === 0
) {
//valuestep
tempArr[i] = children[i];
//
tempMap.set(children[i].stepId, children[i])
tempMap.set(children[i].stepId, children[i]);
}
}
//step
let arr = children.filter(m => {
return !tempMap.get(m.stepId);
}).sort((m, n) => {
//
return m.value.startTime - n.value.startTime;
});
let arr = children
.filter((m) => {
return !tempMap.get(m.stepId);
})
.sort((m, n) => {
//
return m.value.startTime - n.value.startTime;
});
//arr() tempArr
for (let j = 0, i = 0; j < tempArr.length; j++) {
@ -565,21 +615,21 @@ export default {
//
item.children = tempArr;
}
})
});
},
buildReport() {
if (this.report) {
if (this.isNotRunning) {
this.content = JSON.parse(this.report.content);
if (!this.content) {
this.content = {scenarios: []};
this.content = { scenarios: [] };
}
this.formatResult(this.content);
this.getFails();
this.computeTotalTime();
this.loading = false;
} else {
setTimeout(this.getReport, 2000)
setTimeout(this.getReport, 2000);
}
} else {
this.loading = false;
@ -590,10 +640,10 @@ export default {
if (this.isNotRunning) {
this.fails = [];
let array = [];
this.totalTime = 0
this.totalTime = 0;
if (this.content.scenarios) {
this.content.scenarios.forEach((scenario) => {
this.totalTime = this.totalTime + Number(scenario.responseTime)
this.totalTime = this.totalTime + Number(scenario.responseTime);
let failScenario = Object.assign({}, scenario);
if (scenario.error > 0) {
this.fails.push(failScenario);
@ -604,9 +654,9 @@ export default {
failScenario.requestResults.push(failRequest);
array.push(request);
}
})
});
}
})
});
}
this.formatTree(array, this.failsTreeNodes);
this.sort(this.failsTreeNodes);
@ -627,14 +677,14 @@ export default {
}
let resTime;
if (startTime === 0 || endTime === 0) {
resTime = 0
resTime = 0;
} else {
resTime = endTime - startTime
resTime = endTime - startTime;
}
requestTime = requestTime + resTime;
})
})
this.totalTime = requestTime
});
});
this.totalTime = requestTime;
}
},
requestResult(requestResult) {
@ -651,8 +701,11 @@ export default {
});
},
formatExportApi(array, scenario) {
array.forEach(item => {
if (this.stepFilter && this.stepFilter.get("AllSamplerProxy").indexOf(item.type) !== -1) {
array.forEach((item) => {
if (
this.stepFilter &&
this.stepFilter.get('AllSamplerProxy').indexOf(item.type) !== -1
) {
if (item.errorCode) {
item.value.errorCode = item.errorCode;
}
@ -661,30 +714,36 @@ export default {
if (item.children && item.children.length > 0) {
this.formatExportApi(item.children, scenario);
}
})
});
},
handleExport() {
this.getReportByExport();
},
startExport() {
if (this.report.reportVersion && this.report.reportVersion > 1) {
if (this.report.reportType === 'API_INTEGRATED' || this.report.reportType === 'UI_INTEGRATED') {
let scenario = {name: "", requestResults: []};
if (
this.report.reportType === 'API_INTEGRATED' ||
this.report.reportType === 'UI_INTEGRATED'
) {
let scenario = { name: '', requestResults: [] };
this.content.scenarios = [scenario];
this.formatExportApi(this.fullTreeNodes, scenario);
} else {
if (this.fullTreeNodes) {
this.fullTreeNodes.forEach(item => {
if (item.type === "scenario") {
let scenario = {name: item.label, requestResults: []};
if (this.content.scenarios && this.content.scenarios.length > 0) {
this.fullTreeNodes.forEach((item) => {
if (item.type === 'scenario') {
let scenario = { name: item.label, requestResults: [] };
if (
this.content.scenarios &&
this.content.scenarios.length > 0
) {
this.content.scenarios.push(scenario);
} else {
this.content.scenarios = [scenario];
}
this.formatExportApi(item.children, scenario);
}
})
});
}
}
}
@ -693,7 +752,10 @@ export default {
let name = this.report.name;
this.$nextTick(() => {
setTimeout(() => {
downloadPDF(document.getElementById("apiTestReport"), name || "scenario-report");
downloadPDF(
document.getElementById('apiTestReport'),
name || 'scenario-report'
);
reset();
}, 5000);
});
@ -707,14 +769,17 @@ export default {
reportReName({
id: this.report.id,
name: this.report.name,
reportType: this.report.reportType
}).then(response => {
this.$success(this.$t('commons.save_success'));
this.loading = false;
this.$emit('refresh');
}, error => {
this.loading = false;
});
reportType: this.report.reportType,
}).then(
(response) => {
this.$success(this.$t('commons.save_success'));
this.loading = false;
this.$emit('refresh');
},
(error) => {
this.loading = false;
}
);
},
exportReportReset() {
this.reportExportVisible = false;
@ -739,16 +804,16 @@ export default {
},
computed: {
path() {
return "/api/test/edit?id=" + this.report.testId;
return '/api/test/edit?id=' + this.report.testId;
},
isNotRunning() {
return "RUNNING" !== this.report.status;
return 'RUNNING' !== this.report.status;
},
projectId() {
return getCurrentProjectID();
},
}
}
},
};
</script>
<style>
.report-container .el-tabs__header {
@ -757,7 +822,6 @@ export default {
</style>
<style scoped>
.report-container {
height: calc(100vh - 70px);
min-height: 600px;
@ -778,7 +842,7 @@ export default {
}
.report-container .fail {
color: #F56C6C;
color: #f56c6c;
}
.report-container .is-active .fail {
@ -803,6 +867,6 @@ export default {
}
.target-node-item {
background: #FFFFFF;
background: #ffffff;
}
</style>

View File

@ -1,14 +1,32 @@
<template>
<ms-report-export-template :title="title" :report="report" :project-env-map="projectEnvMap"
:type="$t('report.api_test_report')">
<ms-metric-chart :content="content" :is-export="true" :totalTime="totalTime" :report="report"/>
<div class="scenario-result" v-for="(scenario, index) in content.scenarios" :key="index" :scenario="scenario">
<ms-report-export-template
:title="title"
:report="report"
:project-env-map="projectEnvMap"
:type="$t('report.api_test_report')"
>
<ms-metric-chart
:content="content"
:is-export="true"
:totalTime="totalTime"
:report="report"
/>
<div
class="scenario-result"
v-for="(scenario, index) in content.scenarios"
:key="index"
:scenario="scenario"
>
<el-card>
<template v-slot:header>
{{ $t('api_report.scenario_name') }}{{ scenario.name }}
</template>
<div class="ms-border clearfix" v-for="(request, index) in scenario.requestResults" :key="index"
:request="request">
<div
class="ms-border clearfix"
v-for="(request, index) in scenario.requestResults"
:key="index"
:request="request"
>
<div class="request-top">
<div>
{{ getName(request.name) }}
@ -17,13 +35,17 @@
{{ request.url }}
</div>
</div>
<el-divider/>
<el-divider />
<div class="request-middle">
<api-report-request-header-item :title="$t('api_test.request.method')">
<api-report-request-header-item
:title="$t('api_test.request.method')"
>
<span class="method"> {{ request.method }}</span>
</api-report-request-header-item>
<api-report-request-header-item :title="$t('api_report.response_time')">
<api-report-request-header-item
:title="$t('api_report.response_time')"
>
{{ request.responseResult.responseTime }} ms
</api-report-request-header-item>
@ -31,11 +53,15 @@
{{ request.responseResult.latency }} ms
</api-report-request-header-item>
<api-report-request-header-item :title="$t('api_report.request_size')">
<api-report-request-header-item
:title="$t('api_report.request_size')"
>
{{ request.requestSize }} bytes
</api-report-request-header-item>
<api-report-request-header-item :title="$t('api_report.response_size')">
<api-report-request-header-item
:title="$t('api_report.response_size')"
>
{{ request.responseResult.responseSize }} bytes
</api-report-request-header-item>
@ -43,22 +69,29 @@
{{ request.error }}
</api-report-request-header-item>
<api-report-request-header-item :title="$t('api_report.assertions')">
{{ request.passAssertions + " / " + request.totalAssertions }}
<api-report-request-header-item
:title="$t('api_report.assertions')"
>
{{ request.passAssertions + ' / ' + request.totalAssertions }}
</api-report-request-header-item>
<api-report-request-header-item :title="$t('api_report.response_code')">
<api-report-request-header-item
:title="$t('api_report.response_code')"
>
{{ request.responseResult.responseCode }}
</api-report-request-header-item>
<api-report-request-header-item :title="$t('api_report.result')">
<el-tag v-if="request.unexecute">{{
$t('api_test.home_page.detail_card.unexecute')
}}
<el-tag v-if="request.unexecute"
>{{ $t('api_test.home_page.detail_card.unexecute') }}
</el-tag>
<el-tag v-else-if="!request.success && request.status && request.status==='PENDING'">{{
$t('api_test.home_page.detail_card.unexecute')
}}
<el-tag
v-else-if="
!request.success &&
request.status &&
request.status === 'PENDING'
"
>{{ $t('api_test.home_page.detail_card.unexecute') }}
</el-tag>
<el-tag v-else-if="request.errorCode" class="ms-test-error_code">
{{ $t('error_report_library.option.name') }}
@ -66,9 +99,7 @@
<el-tag size="mini" type="success" v-else-if="request.success">
Success
</el-tag>
<el-tag size="mini" type="danger" v-else>
Error
</el-tag>
<el-tag size="mini" type="danger" v-else> Error </el-tag>
</api-report-request-header-item>
</div>
</div>
@ -78,47 +109,51 @@
</template>
<script>
import MsScenarioResult from "./components/ScenarioResult";
import MsRequestResultTail from "./components/RequestResultTail";
import ApiReportRequestHeaderItem from "./ApiReportRequestHeaderItem";
import MsMetricChart from "./components/MetricChart";
import MsReportTitle from "metersphere-frontend/src/components/report/MsReportTitle";
import MsReportExportTemplate from "metersphere-frontend/src/components/report/MsReportExportTemplate";
import MsAssertionResults from "../../../business/automation/report/components/AssertionResults"
import MsScenarioResult from './components/ScenarioResult';
import MsRequestResultTail from './components/RequestResultTail';
import ApiReportRequestHeaderItem from './ApiReportRequestHeaderItem';
import MsMetricChart from './components/MetricChart';
import MsReportTitle from 'metersphere-frontend/src/components/report/MsReportTitle';
import MsReportExportTemplate from 'metersphere-frontend/src/components/report/MsReportExportTemplate';
import MsAssertionResults from '../../../business/automation/report/components/AssertionResults';
export default {
name: "MsApiReportExport",
name: 'MsApiReportExport',
components: {
MsReportExportTemplate,
MsReportTitle, MsMetricChart, ApiReportRequestHeaderItem, MsRequestResultTail, MsScenarioResult, MsAssertionResults
MsReportTitle,
MsMetricChart,
ApiReportRequestHeaderItem,
MsRequestResultTail,
MsScenarioResult,
MsAssertionResults,
},
props: {
report: Object,
content: Object,
totalTime: Number,
projectEnvMap: {},
title: String
title: String,
},
data() {
return {}
return {};
},
methods: {
getName(name) {
if (name && name.indexOf("^@~@^") !== -1) {
let arr = name.split("^@~@^");
if (arr[arr.length - 1].indexOf("UUID=")) {
return arr[arr.length - 1].split("UUID=")[0];
if (name && name.indexOf('^@~@^') !== -1) {
let arr = name.split('^@~@^');
if (arr[arr.length - 1].indexOf('UUID=')) {
return arr[arr.length - 1].split('UUID=')[0];
}
return arr[arr.length - 1];
}
return name;
}
}
}
},
},
};
</script>
<style scoped>
.scenario-result {
margin-top: 20px;
margin-bottom: 20px;
@ -126,17 +161,19 @@ export default {
}
.method {
color: #1E90FF;
color: #1e90ff;
font-size: 14px;
font-weight: 500;
}
.request-top, .request-bottom, .request-middle {
.request-top,
.request-bottom,
.request-middle {
margin-left: 20px;
}
.url {
color: #409EFF;
color: #409eff;
font-size: 14px;
font-weight: bold;
font-style: italic;
@ -152,9 +189,8 @@ export default {
}
.ms-test-error_code {
color: #F6972A;
background-color: #FDF5EA;
border-color: #FDF5EA;
color: #f6972a;
background-color: #fdf5ea;
border-color: #fdf5ea;
}
</style>

View File

@ -3,51 +3,91 @@
<ms-main-container>
<el-card class="table-card" v-loading="result">
<template v-slot:header>
<ms-table-header :condition.sync="condition" v-if="loadIsOver" @search="search" :show-create="false">
<ms-table-header
:condition.sync="condition"
v-if="loadIsOver"
@search="search"
:show-create="false"
>
<template v-slot:button>
<el-button-group>
<el-tooltip class="item" effect="dark" content="left" :disabled="true" placement="left">
<el-button plain :class="{active: leftActive}" @click="changeTab('left')">
<el-tooltip
class="item"
effect="dark"
content="left"
:disabled="true"
placement="left"
>
<el-button
plain
:class="{ active: leftActive }"
@click="changeTab('left')"
>
{{ $t('commons.scenario') }}
</el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="right" :disabled="true" placement="right">
<el-button plain :class="{active: rightActive}" @click="changeTab('right')">
<el-tooltip
class="item"
effect="dark"
content="right"
:disabled="true"
placement="right"
>
<el-button
plain
:class="{ active: rightActive }"
@click="changeTab('right')"
>
{{ $t('api_test.definition.request.case') }}
</el-button>
</el-tooltip>
</el-button-group>
</template>
</ms-table-header>
</template>
<el-table ref="reportListTable" border :data="tableData" class="adjust-table table-content" @sort-change="sort"
@select-all="handleSelectAll"
@select="handleSelect"
:height="screenHeight"
@filter-change="filter" @row-click="handleView" v-if="loadIsOver">
<el-table-column
type="selection"/>
<el-table
ref="reportListTable"
border
:data="tableData"
class="adjust-table table-content"
@sort-change="sort"
@select-all="handleSelectAll"
@select="handleSelect"
:height="screenHeight"
@filter-change="filter"
@row-click="handleView"
v-if="loadIsOver"
>
<el-table-column type="selection" />
<el-table-column width="40" :resizable="false" align="center">
<el-dropdown slot="header" style="width: 14px">
<span class="el-dropdown-link" style="width: 14px">
<i class="el-icon-arrow-down el-icon--right" style="margin-left: 0px"></i>
<i
class="el-icon-arrow-down el-icon--right"
style="margin-left: 0px"
></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native.stop="isSelectDataAll(true)">
{{ $t('api_test.batch_menus.select_all_data', [total]) }}
</el-dropdown-item>
<el-dropdown-item @click.native.stop="isSelectDataAll(false)">
{{ $t('api_test.batch_menus.select_show_data', [tableData.length]) }}
{{
$t('api_test.batch_menus.select_show_data', [
tableData.length,
])
}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
<show-more-btn
:is-show="scope.row.showMore"
:buttons="buttons"
:size="selectDataCounts"
/>
</template>
</el-table-column>
@ -59,12 +99,17 @@
:editable="true"
:edit-content="$t('report.rename_report')"
@editColumn="openReNameDialog"
min-width="200px">
min-width="200px"
>
</ms-table-column>
<el-table-column prop="reportType" :label="$t('load_test.report_type')" width="150"
column-key="reportType"
:filters="reportTypeFilters">
<el-table-column
prop="reportType"
:label="$t('load_test.report_type')"
width="150"
column-key="reportType"
:filters="reportTypeFilters"
>
<template v-slot:default="scope">
<div v-if="scope.row.reportType === 'SCENARIO_INTEGRATED'">
<el-tag size="mini" type="primary">
@ -92,14 +137,29 @@
</div>
</template>
</el-table-column>
<ms-table-column prop="userName" :label="$t('api_test.creator')" width="150" show-overflow-tooltip
:filters="userFilters"/>
<el-table-column prop="createTime" min-width="120" :label="$t('commons.create_time')" sortable>
<ms-table-column
prop="userName"
:label="$t('api_test.creator')"
width="150"
show-overflow-tooltip
:filters="userFilters"
/>
<el-table-column
prop="createTime"
min-width="120"
:label="$t('commons.create_time')"
sortable
>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column prop="endTime" min-width="120" :label="$t('report.test_end_time')" sortable>
<el-table-column
prop="endTime"
min-width="120"
:label="$t('report.test_end_time')"
sortable
>
<template v-slot:default="scope">
<span v-if="scope.row.endTime && scope.row.endTime > 0">
{{ scope.row.endTime | datetimeFormat }}
@ -109,86 +169,132 @@
</span>
</template>
</el-table-column>
<el-table-column prop="triggerMode" width="150" :label="$t('commons.trigger_mode.name')"
column-key="triggerMode" :filters="triggerFilters">
<el-table-column
prop="triggerMode"
width="150"
:label="$t('commons.trigger_mode.name')"
column-key="triggerMode"
:filters="triggerFilters"
>
<template v-slot:default="scope">
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode" />
</template>
</el-table-column>
<el-table-column
:label="$t('commons.status')"
:filters="statusFilters"
column-key="status"
prop="status">
<template v-slot:default="{row}">
<ms-api-report-status :status="row.status"/>
prop="status"
>
<template v-slot:default="{ row }">
<ms-api-report-status :status="row.status" />
</template>
</el-table-column>
<el-table-column width="150" :label="$t('commons.operating')">
<template v-slot:default="scope">
<div>
<ms-table-operator-button
:tip="$t('api_report.detail')" icon="el-icon-s-data"
@exec="handleView(scope.row)" type="primary"/>
:tip="$t('api_report.detail')"
icon="el-icon-s-data"
@exec="handleView(scope.row)"
type="primary"
/>
<ms-table-operator-button
:tip="$t('api_report.delete')"
v-permission="['PROJECT_API_REPORT:READ+DELETE']"
icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger"/>
icon="el-icon-delete"
@exec="handleDelete(scope.row)"
type="danger"
/>
</div>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<ms-table-pagination
:change="search"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
/>
</el-card>
<ms-rename-report-dialog ref="renameDialog" @submit="rename($event)"></ms-rename-report-dialog>
<el-dialog :close-on-click-modal="false" :title="$t('test_track.plan_view.test_result')" width="60%"
:visible.sync="resVisible" class="api-import" destroy-on-close @close="resVisible=false">
<ms-request-result-tail :report-id="reportId" :response="response" ref="debugResult"/>
<ms-rename-report-dialog
ref="renameDialog"
@submit="rename($event)"
></ms-rename-report-dialog>
<el-dialog
:close-on-click-modal="false"
:title="$t('test_track.plan_view.test_result')"
width="60%"
:visible.sync="resVisible"
class="api-import"
destroy-on-close
@close="resVisible = false"
>
<ms-request-result-tail
:report-id="reportId"
:response="response"
ref="debugResult"
/>
</el-dialog>
</ms-main-container>
</ms-container>
</template>
<script>
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {REPORT_CASE_CONFIGS, REPORT_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import {_filter, _sort} from "metersphere-frontend/src/utils/tableUtils";
import MsRenameReportDialog from "metersphere-frontend/src/components/report/MsRenameReportDialog";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import MsRequestResultTail from "@/business/definition/components/response/RequestResultTail";
import MsTabButton from "@/business/commons/MsTabs";
import {getMaintainer} from "@/api/project";
import {delBatchReport, delReport, getReportPage, reportReName} from "@/api/scenario-report";
import {getApiReportPage} from "@/api/definition-report";
import {REPORT_STATUS} from "@/business/commons/js/commons";
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import {
REPORT_CASE_CONFIGS,
REPORT_CONFIGS,
} from 'metersphere-frontend/src/components/search/search-components';
import { _filter, _sort } from 'metersphere-frontend/src/utils/tableUtils';
import MsRenameReportDialog from 'metersphere-frontend/src/components/report/MsRenameReportDialog';
import MsTableColumn from 'metersphere-frontend/src/components/table/MsTableColumn';
import MsRequestResultTail from '@/business/definition/components/response/RequestResultTail';
import MsTabButton from '@/business/commons/MsTabs';
import { getMaintainer } from '@/api/project';
import {
delBatchReport,
delReport,
getReportPage,
reportReName,
} from '@/api/scenario-report';
import { getApiReportPage } from '@/api/definition-report';
import { REPORT_STATUS } from '@/business/commons/js/commons';
export default {
components: {
ReportTriggerModeItem: () => import("metersphere-frontend/src/components/tableItem/ReportTriggerModeItem"),
MsTableOperatorButton: () => import("metersphere-frontend/src/components/MsTableOperatorButton"),
MsApiReportStatus: () => import("./ApiReportStatus"),
MsMainContainer: () => import("metersphere-frontend/src/components/MsMainContainer"),
MsContainer: () => import("metersphere-frontend/src/components/MsContainer"),
MsTableHeader: () => import("metersphere-frontend/src/components/MsTableHeader"),
MsTablePagination: () => import("metersphere-frontend/src/components/pagination/TablePagination"),
ShowMoreBtn: () => import("@/business/commons/ShowMoreBtn"),
ReportTriggerModeItem: () =>
import(
'metersphere-frontend/src/components/tableItem/ReportTriggerModeItem'
),
MsTableOperatorButton: () =>
import('metersphere-frontend/src/components/MsTableOperatorButton'),
MsApiReportStatus: () => import('./ApiReportStatus'),
MsMainContainer: () =>
import('metersphere-frontend/src/components/MsMainContainer'),
MsContainer: () =>
import('metersphere-frontend/src/components/MsContainer'),
MsTableHeader: () =>
import('metersphere-frontend/src/components/MsTableHeader'),
MsTablePagination: () =>
import('metersphere-frontend/src/components/pagination/TablePagination'),
ShowMoreBtn: () => import('@/business/commons/ShowMoreBtn'),
MsRenameReportDialog,
MsTableColumn,
MsTabButton,
MsRequestResultTail,
},
props: {
reportType: String
reportType: String,
},
data() {
return {
result: false,
resVisible: false,
response: {},
reportId: "",
reportId: '',
debugVisible: false,
condition: {
components: REPORT_CONFIGS
components: REPORT_CONFIGS,
},
tableData: [],
multipleSelection: [],
@ -197,29 +303,45 @@ export default {
loadIsOver: true,
total: 0,
loading: false,
currentProjectId: "",
currentProjectId: '',
statusFilters: REPORT_STATUS,
reportTypeFilters: [],
reportScenarioFilters: [
{text: this.$t('api_test.scenario.independent') + this.$t('commons.scenario'), value: 'SCENARIO_INDEPENDENT'},
{text: this.$t('api_test.scenario.integrated') + this.$t('commons.scenario'), value: 'SCENARIO_INTEGRATED'}
{
text:
this.$t('api_test.scenario.independent') +
this.$t('commons.scenario'),
value: 'SCENARIO_INDEPENDENT',
},
{
text:
this.$t('api_test.scenario.integrated') +
this.$t('commons.scenario'),
value: 'SCENARIO_INTEGRATED',
},
],
reportCaseFilters: [
{text: this.$t('api_test.scenario.independent') + 'case', value: 'API_INDEPENDENT'},
{text: this.$t('api_test.scenario.integrated') + 'case', value: 'API_INTEGRATED'},
{
text: this.$t('api_test.scenario.independent') + 'case',
value: 'API_INDEPENDENT',
},
{
text: this.$t('api_test.scenario.integrated') + 'case',
value: 'API_INTEGRATED',
},
],
triggerFilters: [
{text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'},
{text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'},
{text: this.$t('commons.trigger_mode.api'), value: 'API'},
{text: this.$t('api_test.automation.batch_execute'), value: 'BATCH'},
{ text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL' },
{ text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE' },
{ text: this.$t('commons.trigger_mode.api'), value: 'API' },
{ text: this.$t('api_test.automation.batch_execute'), value: 'BATCH' },
],
buttons: [
{
name: this.$t('api_report.batch_delete'),
handleClick: this.handleBatchDelete,
permissions: ['PROJECT_API_REPORT:READ+DELETE']
}
permissions: ['PROJECT_API_REPORT:READ+DELETE'],
},
],
selectRows: new Set(),
selectAll: false,
@ -228,18 +350,18 @@ export default {
screenHeight: 'calc(100vh - 160px)',
activeDom: 'left',
userFilters: [],
}
};
},
watch: {
'$route'(to, from) {
$route(to, from) {
if (to.path.startsWith('/api/automation/report')) {
this.init();
}
},
activeDom() {
this.condition.filters = {report_type: []};
this.condition.filters = { report_type: [] };
this.search();
}
},
},
computed: {
leftActive() {
@ -251,9 +373,9 @@ export default {
},
methods: {
getMaintainerOptions() {
getMaintainer().then(response => {
this.userFilters = response.data.map(u => {
return {text: u.name, value: u.id};
getMaintainer().then((response) => {
this.userFilters = response.data.map((u) => {
return { text: u.name, value: u.id };
});
});
},
@ -276,26 +398,37 @@ export default {
this.genRedirectParams(this.condition);
if (this.activeDom === 'left') {
this.reportTypeFilters = this.reportScenarioFilters;
this.result = getReportPage(this.currentPage, this.pageSize, this.condition).then(res => {
this.result = getReportPage(
this.currentPage,
this.pageSize,
this.condition
).then((res) => {
this.setData(res);
})
});
} else {
this.reportTypeFilters = this.reportCaseFilters;
this.result = getApiReportPage(this.currentPage, this.pageSize, this.condition).then(res => {
this.result = getApiReportPage(
this.currentPage,
this.pageSize,
this.condition
).then((res) => {
this.setData(res);
})
});
}
},
genRedirectParams(condition) {
let selectDataRange = "all";
let selectDataType = "all";
let selectDataRange = 'all';
let selectDataType = 'all';
let routeParamObj = this.$route.params;
if (routeParamObj) {
selectDataRange = routeParamObj.dataSelectRange;
selectDataType = routeParamObj.dataType;
}
if ((this.activeDom === 'left' && selectDataType === 'scenario') || (this.activeDom === 'right' && selectDataType === 'apiCase')) {
if (
(this.activeDom === 'left' && selectDataType === 'scenario') ||
(this.activeDom === 'right' && selectDataType === 'apiCase')
) {
condition.selectDataType = selectDataType;
switch (selectDataRange) {
case 'scheduleExecutionPassCount':
@ -315,36 +448,43 @@ export default {
this.total = data.itemCount;
this.tableData = data.listObject;
this.selectRows.clear();
this.unSelection = data.listObject.map(s => s.id);
this.unSelection = data.listObject.map((s) => s.id);
},
handleView(report) {
this.reportId = report.id;
if (report.status === 'RUNNING' || report.status === 'RERUNNING') {
this.$warning(this.$t('commons.run_warning'))
this.$warning(this.$t('commons.run_warning'));
return;
}
if (report.reportType.indexOf('SCENARIO') !== -1 || report.reportType === 'API_INTEGRATED') {
if (
report.reportType.indexOf('SCENARIO') !== -1 ||
report.reportType === 'API_INTEGRATED'
) {
this.currentProjectId = report.projectId;
this.$router.push({
path: 'report/view/' + report.id,
query: {list: true}
query: { list: true },
});
} else {
this.resVisible = true;
}
},
handleDelete(report) {
this.$alert(this.$t('api_report.delete_confirm') + report.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
delReport(report.id).then(() => {
this.$success(this.$t('commons.delete_success'));
this.search();
});
}
this.$alert(
this.$t('api_report.delete_confirm') + report.name + '',
'',
{
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
delReport(report.id).then(() => {
this.$success(this.$t('commons.delete_success'));
this.search();
});
}
},
}
});
);
},
init() {
this.testId = this.$route.params.testId;
@ -360,54 +500,55 @@ export default {
},
handleSelect(selection, row) {
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.$set(row, 'showMore', false);
this.selectRows.delete(row);
} else {
this.$set(row, "showMore", true);
this.$set(row, 'showMore', true);
this.selectRows.add(row);
}
this.selectRowsCount(this.selectRows)
this.selectRowsCount(this.selectRows);
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.tableData.forEach(item => {
this.$set(item, "showMore", true);
this.tableData.forEach((item) => {
this.$set(item, 'showMore', true);
this.selectRows.add(item);
});
} else {
this.selectRows.clear();
this.tableData.forEach(row => {
this.$set(row, "showMore", false);
})
this.tableData.forEach((row) => {
this.$set(row, 'showMore', false);
});
}
this.selectRowsCount(this.selectRows)
this.selectRowsCount(this.selectRows);
},
handleBatchDelete() {
this.$alert(this.$t('api_report.delete_batch_confirm') + "", '', {
this.$alert(this.$t('api_report.delete_batch_confirm') + '', '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
let ids = Array.from(this.selectRows).map((row) => row.id);
let sendParam = {};
sendParam.ids = ids;
sendParam.selectAllDate = this.isSelectAllDate;
sendParam.unSelectIds = this.unSelection;
sendParam = Object.assign(sendParam, this.condition);
sendParam.caseType = this.activeDom === 'right' ? 'API' : 'SCENARIO';
sendParam.caseType =
this.activeDom === 'right' ? 'API' : 'SCENARIO';
delBatchReport(sendParam).then(() => {
this.selectRows.clear();
this.$success(this.$t('commons.delete_success'));
this.search();
});
}
}
},
});
},
selectRowsCount(selection) {
let selectedIDs = this.getIds(selection);
let allIDs = this.tableData.map(s => s.id);
let allIDs = this.tableData.map((s) => s.id);
this.unSelection = allIDs.filter(function (val) {
return selectedIDs.indexOf(val) === -1
return selectedIDs.indexOf(val) === -1;
});
if (this.isSelectAllDate) {
this.selectDataCounts = this.total - this.unSelection.length;
@ -417,15 +558,15 @@ export default {
},
isSelectDataAll(dataType) {
this.isSelectAllDate = dataType;
this.selectRowsCount(this.selectRows)
this.selectRowsCount(this.selectRows);
//
if (this.selectRows.size != this.tableData.length) {
this.$refs.reportListTable.toggleAllSelection(true);
}
},
getIds(rowSets) {
let rowArray = Array.from(rowSets)
let ids = rowArray.map(s => s.id);
let rowArray = Array.from(rowSets);
let ids = rowArray.map((s) => s.id);
return ids;
},
openReNameDialog($event) {
@ -433,7 +574,7 @@ export default {
},
rename(data) {
reportReName(data).then(() => {
this.$success(this.$t("organization.integration.successful_operation"));
this.$success(this.$t('organization.integration.successful_operation'));
this.init();
this.$refs.renameDialog.close();
});
@ -455,8 +596,8 @@ export default {
created() {
this.init();
this.getMaintainerOptions();
}
}
},
};
</script>
<style scoped>
@ -467,7 +608,7 @@ export default {
.active {
border: solid 1px #6d317c !important;
background-color: var(--primary_color) !important;
color: #FFFFFF !important;
color: #ffffff !important;
}
.item {

View File

@ -1,7 +1,7 @@
<template>
<div class="item">
<div class="item-title">
{{title}}
{{ title }}
</div>
<div>
<slot></slot>
@ -10,22 +10,20 @@
</template>
<script>
export default {
name: "ApiReportRequestHeaderItem",
props: {title: String}
}
export default {
name: 'ApiReportRequestHeaderItem',
props: { title: String },
};
</script>
<style scoped>
.item {
width: 120px;
height: 50px;
display: inline-block;
}
.item {
width: 120px;
height: 50px;
display: inline-block;
}
.item-title {
margin-bottom: 20px;
}
.item-title {
margin-bottom: 20px;
}
</style>

View File

@ -1,21 +1,32 @@
<template>
<div>
<el-tag size="mini" type="primary" effect="plain" v-if="getStatus(status) === 'running'">
<el-tag
size="mini"
type="primary"
effect="plain"
v-if="getStatus(status) === 'running'"
>
{{ showStatus(status) }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="getStatus(status) === 'success'">
<el-tag
size="mini"
type="success"
v-else-if="getStatus(status) === 'success'"
>
{{ showStatus(status) }}
</el-tag>
<el-tag size="mini" type="danger" v-else-if="getStatus(status) === 'error'">
{{ showStatus(status) }}
</el-tag>
<el-tag size="mini" type="danger" style="background-color: #F6972A; color: #FFFFFF"
v-else-if="getStatus(status) === 'fake_error'">
<el-tag
size="mini"
type="danger"
style="background-color: #f6972a; color: #ffffff"
v-else-if="getStatus(status) === 'fake_error'"
>
FakeError
</el-tag>
<span v-else-if="status === '-'" size="mini" type="info">
-
</span>
<span v-else-if="status === '-'" size="mini" type="info"> - </span>
<el-tag v-else size="mini" type="info">
{{ showStatus(status) }}
</el-tag>
@ -24,28 +35,28 @@
<script>
export default {
name: "MsApiReportStatus",
name: 'MsApiReportStatus',
props: {
status: String
status: String,
},
methods: {
getStatus(status) {
if (status) {
return status.toLowerCase();
}
return "PENDING";
return 'PENDING';
},
showStatus(status) {
if (!status) {
status = 'PENDING';
}
return status.toLowerCase()[0].toUpperCase() + status.toLowerCase().substr(1);
}
}
}
return (
status.toLowerCase()[0].toUpperCase() + status.toLowerCase().substr(1)
);
},
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -5,15 +5,16 @@
:is-share="isShare"
:is-plan="isPlanReport"
:template-report="response"
:is-template="isTemplate"/>
:is-template="isTemplate"
/>
</template>
<script>
import MsApiReport from "@/business/automation/report/ApiReportDetail";
import MsApiReport from '@/business/automation/report/ApiReportDetail';
export default {
name: "ApiReportView",
components: {MsApiReport},
name: 'ApiReportView',
components: { MsApiReport },
computed: {
reportIdByPath() {
return this.getRouteParam('reportId');
@ -37,16 +38,13 @@ export default {
methods: {
getRouteParam(name) {
if (this.$route) {
return this.$route.params[name]
return this.$route.params[name];
} else {
return null;
}
}
}
}
},
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -3,64 +3,115 @@
<el-row>
<el-col>
<span v-if="!debug">
<el-input v-if="nameIsEdit" size="mini" @blur="handleSave(report.name)" @keyup.enter.native="handleSaveKeyUp"
style="width: 200px" v-model="report.name" maxlength="60" show-word-limit/>
<el-input
v-if="nameIsEdit"
size="mini"
@blur="handleSave(report.name)"
@keyup.enter.native="handleSaveKeyUp"
style="width: 200px"
v-model="report.name"
maxlength="60"
show-word-limit
/>
<span v-else>
<el-link v-if="isSingleScenario"
type="primary"
class="report-name"
@click="redirect">
<el-link
v-if="isSingleScenario"
type="primary"
class="report-name"
@click="redirect"
>
{{ report.name }}
</el-link>
<span v-else>
{{ report.name }}
</span>
<i v-if="showCancelButton" class="el-icon-edit" style="cursor:pointer" @click="nameIsEdit = true"
@click.stop/>
<i
v-if="showCancelButton"
class="el-icon-edit"
style="cursor: pointer"
@click="nameIsEdit = true"
@click.stop
/>
</span>
</span>
<span v-if="report.endTime || report.createTime">
<span style="margin-left: 10px">{{ $t('report.test_start_time') }}</span>
<span style="margin-left: 10px"
>{{ $t('report.test_start_time') }}</span
>
<span class="time"> {{ report.createTime | datetimeFormat }}</span>
<span style="margin-left: 10px">{{ $t('report.test_end_time') }}</span>
<span style="margin-left: 10px"
>{{ $t('report.test_end_time') }}</span
>
<span class="time"> {{ report.endTime | datetimeFormat }}</span>
</span>
<div style="float: right">
<el-button v-if="!isPlan && (!debug || exportFlag) && !isTemplate"
v-permission="['PROJECT_API_REPORT:READ+EXPORT']" :disabled="isReadOnly" class="export-button"
plain type="primary" size="mini" @click="handleExport(report.name)" style="margin-right: 10px">
<el-button
v-if="!isPlan && (!debug || exportFlag) && !isTemplate"
v-permission="['PROJECT_API_REPORT:READ+EXPORT']"
:disabled="isReadOnly"
class="export-button"
plain
type="primary"
size="mini"
@click="handleExport(report.name)"
style="margin-right: 10px"
>
{{ $t('test_track.plan_view.export_report') }}
</el-button>
<el-popover
v-if="!isPlan && (!debug || exportFlag) && !isTemplate"
v-permission="['PROJECT_PERFORMANCE_REPORT:READ+EXPORT']"
style="margin-right: 10px;float: right;"
style="margin-right: 10px; float: right"
placement="bottom"
trigger="click"
width="300">
width="300"
>
<p>{{ shareUrl }}</p>
<span style="color: red;float: left;margin-left: 10px;" v-if="application.typeValue">{{
$t('commons.validity_period') + application.typeValue
}}</span>
<span
style="color: red; float: left; margin-left: 10px"
v-if="application.typeValue"
>{{ $t('commons.validity_period') + application.typeValue }}</span
>
<div style="text-align: right; margin: 0">
<el-button type="primary" size="mini" :disabled="!shareUrl"
v-clipboard:copy="shareUrl">{{ $t("commons.copy") }}
<el-button
type="primary"
size="mini"
:disabled="!shareUrl"
v-clipboard:copy="shareUrl"
>{{ $t('commons.copy') }}
</el-button>
</div>
<template v-slot:reference>
<el-button :disabled="isReadOnly" type="danger" plain size="mini"
@click="handleShare(report)">
<el-button
:disabled="isReadOnly"
type="danger"
plain
size="mini"
@click="handleShare(report)"
>
{{ $t('test_track.plan_view.share_report') }}
</el-button>
</template>
</el-popover>
<el-button v-if="showRerunButton" class="rerun-button" plain size="mini" @click="rerun">
<el-button
v-if="showRerunButton"
class="rerun-button"
plain
size="mini"
@click="rerun"
>
{{ $t('api_test.automation.rerun') }}
</el-button>
<el-button v-if="showCancelButton" class="export-button" plain size="mini" @click="returnView">
<el-button
v-if="showCancelButton"
class="export-button"
plain
size="mini"
@click="returnView"
>
{{ $t('commons.cancel') }}
</el-button>
</div>
@ -68,28 +119,42 @@
</el-row>
<el-row v-if="showProjectEnv" type="flex">
<span> {{ $t('commons.environment') + ':' }} </span>
<div v-for="(values,key) in projectEnvMap" :key="key" style="margin-right: 10px">
{{ key + ":" }}
<ms-tag v-for="(item,index) in values" :key="index" type="success" :content="item"
style="margin-left: 2px"/>
<div
v-for="(values, key) in projectEnvMap"
:key="key"
style="margin-right: 10px"
>
{{ key + ':' }}
<ms-tag
v-for="(item, index) in values"
:key="index"
type="success"
:content="item"
style="margin-left: 2px"
/>
</div>
</el-row>
</header>
</template>
<script>
import {generateShareInfoWithExpired, getShareRedirectUrl} from "../../../api/share";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {getProjectApplicationConfig} from "../../../api/project";
import {apiTestReRun} from "../../../api/xpack";
import {getUUID} from "metersphere-frontend/src/utils";
import {getApiScenarioIdByPlanScenarioId} from "@/api/test-plan";
import {
generateShareInfoWithExpired,
getShareRedirectUrl,
} from '../../../api/share';
import {
getCurrentProjectID,
getCurrentWorkspaceId,
} from 'metersphere-frontend/src/utils/token';
import MsTag from 'metersphere-frontend/src/components/MsTag';
import { getProjectApplicationConfig } from '../../../api/project';
import { apiTestReRun } from '../../../api/xpack';
import { getUUID } from 'metersphere-frontend/src/utils';
import { getApiScenarioIdByPlanScenarioId } from '@/api/test-plan';
export default {
name: "MsApiReportViewHeader",
components: {MsTag},
name: 'MsApiReportViewHeader',
components: { MsTag },
props: {
report: {},
projectEnvMap: {},
@ -107,20 +172,20 @@ export default {
type: Boolean,
default: false,
},
isPlan: Boolean
isPlan: Boolean,
},
computed: {
showProjectEnv() {
return this.projectEnvMap && JSON.stringify(this.projectEnvMap) !== '{}';
},
path() {
return "/api/test/edit?id=" + this.report.testId;
return '/api/test/edit?id=' + this.report.testId;
},
scenarioId() {
if (typeof this.report.scenarioId === 'string') {
return this.report.scenarioId;
} else {
return "";
return '';
}
},
isSingleScenario() {
@ -136,9 +201,9 @@ export default {
return {
isReadOnly: false,
nameIsEdit: false,
shareUrl: "",
application: {}
}
shareUrl: '',
application: {},
};
},
methods: {
handleExport(name) {
@ -162,21 +227,25 @@ export default {
let uuid = getUUID().substring(1, 5);
let projectId = getCurrentProjectID();
let workspaceId = getCurrentWorkspaceId();
let prefix = "/#";
if (this.$route && this.$route.path.startsWith('/api/automation/report')
&& this.$route.query && this.$route.query.list) {
prefix = ""
let prefix = '/#';
if (
this.$route &&
this.$route.path.startsWith('/api/automation/report') &&
this.$route.query &&
this.$route.query.list
) {
prefix = '';
}
let path = `${prefix}/api/automation/?redirectID=${uuid}&dataType=scenario&projectId=${projectId}&workspaceId=${workspaceId}&resourceId=${resourceId}`;
let data = this.$router.resolve({
path: path
path: path,
});
window.open(data.href, '_blank');
},
rerun() {
let type = this.report.reportType;
let rerunObj = {type: type, reportId: this.report.id}
apiTestReRun(rerunObj).then(res => {
let rerunObj = { type: type, reportId: this.report.id };
apiTestReRun(rerunObj).then((res) => {
if (res.data !== 'SUCCESS') {
this.$error(res.data);
} else {
@ -201,16 +270,27 @@ export default {
});
},
getProjectApplication() {
getProjectApplicationConfig(getCurrentProjectID(), "/API_SHARE_REPORT_TIME").then(res => {
getProjectApplicationConfig(
getCurrentProjectID(),
'/API_SHARE_REPORT_TIME'
).then((res) => {
if (res.data && res.data.typeValue) {
let quantity = res.data.typeValue.substring(0, res.data.typeValue.length - 1);
let unit = res.data.typeValue.substring(res.data.typeValue.length - 1);
let quantity = res.data.typeValue.substring(
0,
res.data.typeValue.length - 1
);
let unit = res.data.typeValue.substring(
res.data.typeValue.length - 1
);
if (unit === 'H') {
res.data.typeValue = quantity + this.$t('commons.date_unit.hour');
} else if (unit === 'D') {
res.data.typeValue = quantity + this.$t('commons.date_unit.day');
} else if (unit === 'M') {
res.data.typeValue = quantity + this.$t('commons.workspace_unit') + this.$t('commons.date_unit.month');
res.data.typeValue =
quantity +
this.$t('commons.workspace_unit') +
this.$t('commons.date_unit.month');
} else if (unit === 'Y') {
res.data.typeValue = quantity + this.$t('commons.date_unit.year');
}
@ -218,12 +298,11 @@ export default {
}
});
},
}
}
},
};
</script>
<style scoped>
.export-button {
float: right;
margin-right: 10px;
@ -232,8 +311,8 @@ export default {
.rerun-button {
float: right;
margin-right: 10px;
background-color: #F2F9EF;
color: #87C45D;
background-color: #f2f9ef;
color: #87c45d;
}
.report-name {

View File

@ -11,14 +11,16 @@
:report="report"
:project-env-map="projectEnvMap"
@reportExport="handleExport"
@reportSave="handleSave"/>
@reportSave="handleSave"
/>
</div>
<main>
<ms-metric-chart
:content="content"
:totalTime="totalTime"
:report="report"
v-if="!loading"/>
v-if="!loading"
/>
<div>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="All" name="total">
@ -31,9 +33,7 @@
/>
</el-tab-pane>
<el-tab-pane name="fail">
<template slot="label">
Error
</template>
<template slot="label"> Error </template>
<ms-scenario-results
:console="content.console"
:report="report"
@ -44,24 +44,26 @@
</el-tab-pane>
<el-tab-pane name="errorReport" v-if="content.errorCode > 0">
<template slot="label">
<span class="fail" style="color: #F6972A">
FakeError
</span>
<span class="fail" style="color: #f6972a"> FakeError </span>
</template>
<ms-scenario-results v-on:requestResult="requestResult" :console="content.console"
:treeData="fullTreeNodes" ref="errorReportTree"/>
<ms-scenario-results
v-on:requestResult="requestResult"
:console="content.console"
:treeData="fullTreeNodes"
ref="errorReportTree"
/>
</el-tab-pane>
<el-tab-pane name="unExecute" v-if="content.unExecute > 0">
<template slot="label">
<span class="fail" style="color: #9C9B9A">
Pending
</span>
<span class="fail" style="color: #9c9b9a"> Pending </span>
</template>
<ms-scenario-results v-on:requestResult="requestResult"
:report="report"
:console="content.console"
:treeData="fullTreeNodes"
ref="unExecuteTree"/>
<ms-scenario-results
v-on:requestResult="requestResult"
:report="report"
:console="content.console"
:treeData="fullTreeNodes"
ref="unExecuteTree"
/>
</el-tab-pane>
<el-tab-pane name="console">
<template slot="label">
@ -87,35 +89,39 @@
</template>
<script>
import {baseSocket} from "@/api/base-network";
import MsRequestResult from "./components/RequestResult";
import MsRequestResultTail from "./components/RequestResultTail";
import MsScenarioResult from "./components/ScenarioResult";
import MsMetricChart from "./components/MetricChart";
import MsScenarioResults from "./components/ScenarioResults";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import MsApiReportExport from "./ApiReportExport";
import MsApiReportViewHeader from "./ApiReportViewHeader";
import {RequestFactory} from "../../definition/model/ApiTestModel";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {windowPrint} from "metersphere-frontend/src/utils";
import {STEP} from "../scenario/Setting";
import {getScenarioReport, updateReport} from "@/api/scenario-report";
import { baseSocket } from '@/api/base-network';
import MsRequestResult from './components/RequestResult';
import MsRequestResultTail from './components/RequestResultTail';
import MsScenarioResult from './components/ScenarioResult';
import MsMetricChart from './components/MetricChart';
import MsScenarioResults from './components/ScenarioResults';
import MsContainer from 'metersphere-frontend/src/components/MsContainer';
import MsMainContainer from 'metersphere-frontend/src/components/MsMainContainer';
import MsApiReportExport from './ApiReportExport';
import MsApiReportViewHeader from './ApiReportViewHeader';
import { RequestFactory } from '../../definition/model/ApiTestModel';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { windowPrint } from 'metersphere-frontend/src/utils';
import { STEP } from '../scenario/Setting';
import { getScenarioReport, updateReport } from '@/api/scenario-report';
export default {
name: "SyncApiReportDetail",
name: 'SyncApiReportDetail',
components: {
MsApiReportViewHeader,
MsApiReportExport,
MsMainContainer,
MsContainer, MsScenarioResults, MsRequestResultTail, MsMetricChart, MsScenarioResult, MsRequestResult
MsContainer,
MsScenarioResults,
MsRequestResultTail,
MsMetricChart,
MsScenarioResult,
MsRequestResult,
},
data() {
return {
activeName: "total",
content: {total: 0, scenarioTotal: 1},
activeName: 'total',
content: { total: 0, scenarioTotal: 1 },
report: {},
loading: false,
totalTime: 0,
@ -128,26 +134,25 @@ export default {
reportExportVisible: false,
requestType: undefined,
fullTreeNodes: [],
debugResult: new Map,
scenarioMap: new Map,
debugResult: new Map(),
scenarioMap: new Map(),
exportFlag: false,
messageWebSocket: {},
websocket: {},
stepFilter: new STEP,
stepFilter: new STEP(),
tempResult: [],
projectEnvMap: {},
}
};
},
activated() {
this.isRequestResult = false;
},
created() {
},
created() {},
mounted() {
this.$nextTick(() => {
if (this.scenario && this.scenario.scenarioDefinition) {
this.content.scenarioStepTotal = this.scenario.scenarioDefinition.hashTree.length;
this.content.scenarioStepTotal =
this.scenario.scenarioDefinition.hashTree.length;
this.initTree();
this.initMessageSocket();
this.clearDebug();
@ -162,21 +167,31 @@ export default {
scenarioId: String,
isUi: {
type: Boolean,
default: false
}
default: false,
},
},
methods: {
initTree() {
this.fullTreeNodes = [];
let obj = {
resId: "root",
resId: 'root',
index: 1,
label: this.scenario.name,
value: {responseResult: {}, unexecute: true, testing: false, status: 'PENDING'},
value: {
responseResult: {},
unexecute: true,
testing: false,
status: 'PENDING',
},
children: [],
unsolicited: true
unsolicited: true,
};
this.formatContent(this.scenario.scenarioDefinition.hashTree, obj, "", "root");
this.formatContent(
this.scenario.scenarioDefinition.hashTree,
obj,
'',
'root'
);
this.fullTreeNodes.push(obj);
},
compare() {
@ -184,23 +199,23 @@ export default {
let v1 = a.value.sort;
let v2 = b.value.sort;
return v1 - v2;
}
};
},
getType(type) {
switch (type) {
case "LoopController":
return "循环控制器";
case "TransactionController":
return "事物控制器";
case "ConstantTimer":
return "等待控制器";
case "IfController":
return "条件控制器";
case 'LoopController':
return '循环控制器';
case 'TransactionController':
return '事物控制器';
case 'ConstantTimer':
return '等待控制器';
case 'IfController':
return '条件控制器';
}
return type;
},
margeTransaction(item, arr) {
arr.forEach(subItem => {
arr.forEach((subItem) => {
if (item.resId === subItem.resourceId) {
item.value = subItem;
item.testing = false;
@ -209,32 +224,59 @@ export default {
if (subItem.subRequestResults && subItem.subRequestResults.length > 0) {
this.margeTransaction(item, subItem.subRequestResults);
}
})
});
},
formatContent(hashTree, tree, fullPath, pid) {
if (hashTree) {
hashTree.forEach(item => {
hashTree.forEach((item) => {
if (item.enable) {
item.parentIndex = fullPath ? fullPath + "_" + item.index : item.index;
item.parentIndex = fullPath
? fullPath + '_' + item.index
: item.index;
let name = item.name ? item.name : this.getType(item.type);
let id = item.type === 'JSR223Processor' || !item.id ? item.resourceId : item.id
let id =
item.type === 'JSR223Processor' || !item.id
? item.resourceId
: item.id;
let obj = {
pid: pid, resId: id + "_" + item.parentIndex, index: Number(item.index), label: name,
value: {name: name, responseResult: {}, unexecute: true, testing: false, status: 'PENDING'}, children:
[], unsolicited: true
pid: pid,
resId: id + '_' + item.parentIndex,
index: Number(item.index),
label: name,
value: {
name: name,
responseResult: {},
unexecute: true,
testing: false,
status: 'PENDING',
},
children: [],
unsolicited: true,
};
tree.children.push(obj);
if (this.stepFilter.get("AllSamplerProxy").indexOf(item.type) !== -1) {
if (
this.stepFilter.get('AllSamplerProxy').indexOf(item.type) !== -1
) {
obj.unsolicited = false;
obj.type = item.type;
} else if (item.type === 'scenario') {
this.content.scenarioTotal += 1;
}
if (item.hashTree && item.hashTree.length > 0 && this.stepFilter && this.stepFilter.get("AllSamplerProxy").indexOf(item.type) === -1) {
this.formatContent(item.hashTree, obj, item.parentIndex, obj.resId);
if (
item.hashTree &&
item.hashTree.length > 0 &&
this.stepFilter &&
this.stepFilter.get('AllSamplerProxy').indexOf(item.type) === -1
) {
this.formatContent(
item.hashTree,
obj,
item.parentIndex,
obj.resId
);
}
}
})
});
}
},
handleExport() {
@ -246,12 +288,12 @@ export default {
});
},
filter(index) {
if (index === "1") {
if (index === '1') {
this.$refs.failsTree.filter(index);
} else if (this.activeName === "errorReport") {
this.$refs.errorReportTree.filter("FAKE_ERROR");
} else if (this.activeName === "unExecute") {
this.$refs.unExecuteTree.filter("PENDING");
} else if (this.activeName === 'errorReport') {
this.$refs.errorReportTree.filter('FAKE_ERROR');
} else if (this.activeName === 'unExecute') {
this.$refs.unExecuteTree.filter('PENDING');
}
},
handleClick(tab, event) {
@ -312,14 +354,17 @@ export default {
}
this.loading = true;
this.report.projectId = this.projectId;
let url = "/api/scenario/report/update";
this.result = updateReport(this.report).then(response => {
this.$success(this.$t('commons.save_success'));
this.loading = false;
this.$emit('refresh');
}, error => {
this.loading = false;
});
let url = '/api/scenario/report/update';
this.result = updateReport(this.report).then(
(response) => {
this.$success(this.$t('commons.save_success'));
this.loading = false;
this.$emit('refresh');
},
(error) => {
this.loading = false;
}
);
},
initMessageSocket() {
@ -328,25 +373,29 @@ export default {
this.messageWebSocket.onerror = this.cleanHeartBeat;
},
getReport() {
getScenarioReport(this.reportId).then(response => {
getScenarioReport(this.reportId).then((response) => {
this.report = response.data || {};
if (response.data) {
this.content = JSON.parse(response.data.content);
if (!this.content) {
this.content = {scenarios: []};
this.content = { scenarios: [] };
}
if (this.content.projectEnvMap) {
this.projectEnvMap = this.content.projectEnvMap;
}
this.content.error = this.content ? this.content.error : "";
this.content.error = this.content ? this.content.error : '';
this.content.success = (this.content.total - this.content.error - this.content.errorCode - this.content.unExecute);
this.content.success =
this.content.total -
this.content.error -
this.content.errorCode -
this.content.unExecute;
this.totalTime = this.content.totalTime;
this.fullTreeNodes = this.content.steps;
this.recursiveSorting(this.fullTreeNodes);
this.reload();
}
if ("Running" !== this.report.status) {
if ('Running' !== this.report.status) {
this.$emit('finish');
}
});
@ -357,39 +406,45 @@ export default {
}
if (Array.isArray(origin)) {
this.sortChildren(origin);
origin.forEach(v => {
origin.forEach((v) => {
if (v.children) {
this.checkOrder(v.children)
this.checkOrder(v.children);
}
})
});
}
},
sortChildren(source) {
if (!source) {
return;
}
source.forEach(item => {
source.forEach((item) => {
let children = item.children;
if (children && children.length > 0) {
let tempArr = new Array(children.length);
let tempMap = new Map();
for (let i = 0; i < children.length; i++) {
if (!children[i].value || !children[i].value.startTime || children[i].value.startTime === 0) {
if (
!children[i].value ||
!children[i].value.startTime ||
children[i].value.startTime === 0
) {
//valuestep
tempArr[i] = children[i];
//
tempMap.set(children[i].stepId, children[i])
tempMap.set(children[i].stepId, children[i]);
}
}
//step
let arr = children.filter(m => {
return !tempMap.get(m.stepId);
}).sort((m, n) => {
//
return m.value.startTime - n.value.startTime;
});
let arr = children
.filter((m) => {
return !tempMap.get(m.stepId);
})
.sort((m, n) => {
//
return m.value.startTime - n.value.startTime;
});
//arr() tempArr
for (let j = 0, i = 0; j < tempArr.length; j++) {
@ -405,13 +460,13 @@ export default {
//
item.children = tempArr;
}
})
});
},
runningNodeChild(arr, resourceId) {
arr.forEach(item => {
arr.forEach((item) => {
if (resourceId === item.resId) {
item.value.testing = true;
} else if (resourceId && resourceId.startsWith("result_")) {
} else if (resourceId && resourceId.startsWith('result_')) {
let data = JSON.parse(resourceId.substring(7));
if (!data.status && data.error > 0) {
data.status = 'ERROR';
@ -419,7 +474,11 @@ export default {
if (!data.status && data.success) {
data.status = 'SUCCESS';
}
if (data.method === 'Request' && data.subRequestResults && data.subRequestResults.length > 0) {
if (
data.method === 'Request' &&
data.subRequestResults &&
data.subRequestResults.length > 0
) {
this.margeTransaction(item, data.subRequestResults);
} else if (item.resId === data.resourceId) {
if (item.value && item.value.id && !item.mark) {
@ -440,14 +499,14 @@ export default {
if (item.children && item.children.length > 0) {
this.runningNodeChild(item.children, resourceId);
}
})
});
},
runningEvaluation(resourceId) {
if (this.fullTreeNodes) {
this.fullTreeNodes.forEach(item => {
this.fullTreeNodes.forEach((item) => {
if (resourceId === item.resId) {
item.value.testing = true;
} else if (resourceId && resourceId.startsWith("result_")) {
} else if (resourceId && resourceId.startsWith('result_')) {
let data = JSON.parse(resourceId.substring(7));
if (!data.status && data.error > 0) {
data.status = 'ERROR';
@ -455,14 +514,18 @@ export default {
if (!data.status && data.success) {
data.status = 'SUCCESS';
}
if (data.method === 'Request' && data.subRequestResults && data.subRequestResults.length > 0) {
data.subRequestResults.forEach(subItem => {
if (
data.method === 'Request' &&
data.subRequestResults &&
data.subRequestResults.length > 0
) {
data.subRequestResults.forEach((subItem) => {
if (item.resId === subItem.resourceId) {
item.value = subItem;
item.testing = false;
item.debug = true;
}
})
});
} else if (item.resId === data.resourceId) {
item.value = data;
item.testing = false;
@ -472,7 +535,7 @@ export default {
if (item.children && item.children.length > 0) {
this.runningNodeChild(item.children, resourceId);
}
})
});
}
},
onMessage(e) {
@ -480,7 +543,7 @@ export default {
this.runningEvaluation(e.data);
this.sort(this.fullTreeNodes);
}
if (e.data && e.data.indexOf("MS_TEST_END") !== -1) {
if (e.data && e.data.indexOf('MS_TEST_END') !== -1) {
this.getReport();
this.messageWebSocket.close();
this.cleanHeartBeat();
@ -503,17 +566,17 @@ export default {
heartBeat() {
let msg = {
reportId: this.reportId,
content: "i'm alive"
content: "i'm alive",
};
this.messageWebSocket.send(JSON.stringify(msg));
}
},
},
computed: {
projectId() {
return getCurrentProjectID();
},
}
}
},
};
</script>
<style>
.report-container .el-tabs__header {
@ -522,7 +585,6 @@ export default {
</style>
<style scoped>
.report-container {
height: calc(100vh - 155px);
min-height: 600px;
@ -543,7 +605,7 @@ export default {
}
.report-container .fail {
color: #F56C6C;
color: #f56c6c;
}
.report-container .is-active .fail {
@ -561,5 +623,4 @@ export default {
.report-body {
min-width: 750px !important;
}
</style>

View File

@ -1,40 +1,77 @@
<template>
<div>
<el-table :data="assertions" :row-style="getRowStyle" :header-cell-style="getRowStyle">
<el-table-column prop="name" :label="$t('api_report.assertions_name')" width="150" show-overflow-tooltip>
<el-table
:data="assertions"
:row-style="getRowStyle"
:header-cell-style="getRowStyle"
>
<el-table-column
prop="name"
:label="$t('api_report.assertions_name')"
width="150"
show-overflow-tooltip
>
<template v-slot:default="scope">
<span>{{ !scope.row.name || scope.row.name === 'null' ? "" : scope.row.name }}</span>
<span>{{
!scope.row.name || scope.row.name === 'null' ? '' : scope.row.name
}}</span>
</template>
</el-table-column>
<el-table-column prop="content" v-if="showContent" :label="$t('api_report.assertions_content')" width="300"
show-overflow-tooltip/>
<el-table-column prop="message" :label="$t('api_report.assertions_error_message')"/>
<el-table-column prop="pass" :label="$t('api_report.assertions_is_success')" width="180">
<template v-slot:default="{row}">
<el-tag size="mini" type="success" v-if="row.pass">
Success
</el-tag>
<el-tag size="mini" type="danger" v-else>
Error
</el-tag>
<el-table-column
prop="content"
v-if="showContent"
:label="$t('api_report.assertions_content')"
width="300"
show-overflow-tooltip
/>
<el-table-column
prop="message"
:label="$t('api_report.assertions_error_message')"
/>
<el-table-column
prop="pass"
:label="$t('api_report.assertions_is_success')"
width="180"
>
<template v-slot:default="{ row }">
<el-tag size="mini" type="success" v-if="row.pass"> Success </el-tag>
<el-tag size="mini" type="danger" v-else> Error </el-tag>
</template>
</el-table-column>
<el-table-column prop="script">
<template v-slot:default="{row}">
<template v-slot:default="{ row }">
<div class="assertion-item btn circle" v-if="row.script">
<i class="el-icon-view el-button el-button--primary el-button--mini is-circle" circle
@click="showPage(row.script)"/>
<i
class="el-icon-view el-button el-button--primary el-button--mini is-circle"
circle
@click="showPage(row.script)"
/>
</div>
</template>
</el-table-column>
</el-table>
<el-dialog :title="$t('api_test.request.assertions.script')" :visible.sync="visible" width="900px" modal-append-to-body append-to-body>
<el-row type="flex" justify="space-between" align="middle" class="quick-script-block">
<el-dialog
:title="$t('api_test.request.assertions.script')"
:visible.sync="visible"
width="900px"
modal-append-to-body
append-to-body
>
<el-row
type="flex"
justify="space-between"
align="middle"
class="quick-script-block"
>
<el-col :span="codeSpan" class="script-content">
<ms-code-edit v-if="isCodeEditAlive"
:read-only="disabled"
:data.sync="scriptContent" theme="eclipse" :modes="['java','python']"
ref="codeEdit"/>
<ms-code-edit
v-if="isCodeEditAlive"
:read-only="disabled"
:data.sync="scriptContent"
theme="eclipse"
:modes="['java', 'python']"
ref="codeEdit"
/>
</el-col>
</el-row>
</el-dialog>
@ -42,18 +79,17 @@
</template>
<script>
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
import MsCodeEdit from 'metersphere-frontend/src/components/MsCodeEdit';
export default {
name: "MsAssertionResults",
components: {MsCodeEdit},
name: 'MsAssertionResults',
components: { MsCodeEdit },
props: {
assertions: Array,
showContent: {
type: Boolean,
default: true
}
default: true,
},
},
data() {
return {
@ -62,11 +98,11 @@ export default {
codeSpan: 20,
isCodeEditAlive: true,
scriptContent: '',
}
};
},
methods: {
getRowStyle() {
return {backgroundColor: "#F5F5F5"};
return { backgroundColor: '#F5F5F5' };
},
showPage(script) {
this.disabled = true;
@ -79,13 +115,10 @@ export default {
this.$nextTick(() => (this.isCodeEditAlive = true));
},
},
}
};
</script>
<style scoped>
.assertion-item.btn.circle {
text-align: right;
min-width: 80px;

View File

@ -10,32 +10,48 @@
</el-row>
<!-- 导出报告样式定制 -->
<div v-if="isExport">
<span class="ms-req ms-req-error"
v-if="(content.error && content.error > 0 )
|| (content.errorCode && content.errorCode > 0)
|| (content.unExecute && content.unExecute > 0)">
<span class="ms-req-span">
{{ content.success + content.error + content.errorCode + content.unExecute }}
{{ $t('api_report.request') }}
</span>
</span>
<span
class="ms-req ms-req-error"
v-if="
(content.error && content.error > 0) ||
(content.errorCode && content.errorCode > 0) ||
(content.unExecute && content.unExecute > 0)
"
>
<span class="ms-req-span">
{{
content.success +
content.error +
content.errorCode +
content.unExecute
}}
{{ $t('api_report.request') }}
</span>
</span>
<span class="ms-req ms-req-success" v-else>
<span class="ms-req-span">
{{ content.success ? content.success + content.error : 0 }} {{ $t('api_report.request') }}
</span>
<span class="ms-req-span">
{{ content.success ? content.success + content.error : 0 }}
{{ $t('api_report.request') }}
</span>
</span>
</div>
<!-- 饼图显示/成功/失败/误报 -->
<ms-chart ref="chart"
:options="options"
:height="150"
:width="150"
:autoresize="true" v-else/>
<ms-chart
ref="chart"
:options="options"
:height="150"
:width="150"
:autoresize="true"
v-else
/>
<!-- 总数统计 -->
<el-row type="flex" justify="center" align="middle">
<div style="min-width: 120px">
<div class="metric-icon-box">
<span class="ms-point-success" style="margin: 7px;float: left;"/>
<span
class="ms-point-success"
style="margin: 7px; float: left"
/>
<div class="metric-box-total">
<div class="value" style="font-size: 12px">
{{ content.success }} Success
@ -44,7 +60,7 @@
</div>
<el-divider></el-divider>
<div class="metric-icon-box" style="height: 26px">
<span class="ms-point-error" style="margin: 7px;float: left;"/>
<span class="ms-point-error" style="margin: 7px; float: left" />
<div class="metric-box-total">
<div class="value" style="font-size: 12px">
{{ content.error }} Error
@ -52,8 +68,15 @@
</div>
</div>
<el-divider v-if="content.errorCode > 0"></el-divider>
<div class="metric-icon-box" v-if="content.errorCode > 0" style="height: 26px">
<span class="ms-point-error-code" style="margin: 7px;float: left;"/>
<div
class="metric-icon-box"
v-if="content.errorCode > 0"
style="height: 26px"
>
<span
class="ms-point-error-code"
style="margin: 7px; float: left"
/>
<div class="metric-box-total" v-if="content.errorCode > 0">
<div class="value" style="font-size: 12px">
{{ content.errorCode }} FakeError
@ -61,8 +84,15 @@
</div>
</div>
<el-divider v-if="content.unExecute > 0"></el-divider>
<div class="metric-icon-box" style="height: 26px" v-if="content.unExecute > 0">
<span class="ms-point-unexecute" style="margin: 7px;float: left;"/>
<div
class="metric-icon-box"
style="height: 26px"
v-if="content.unExecute > 0"
>
<span
class="ms-point-unexecute"
style="margin: 7px; float: left"
/>
<div class="metric-box-total">
<div class="value" style="font-size: 12px">
{{ content.unExecute }} Pending
@ -76,65 +106,120 @@
<div class="split"></div>
<!-- 场景统计 -->
<div style="width: 50%">
<el-row type="flex" justify="center" align="middle" v-if="report.reportType !== 'API_INTEGRATED'">
<el-row
type="flex"
justify="center"
align="middle"
v-if="report.reportType !== 'API_INTEGRATED'"
>
<div class="metric-box">
<div class="value">{{ content.scenarioTotal ? content.scenarioTotal : 0 }}</div>
<div class="value">
{{ content.scenarioTotal ? content.scenarioTotal : 0 }}
</div>
<div class="name">{{ $t('api_test.scenario.scenario') }}</div>
</div>
<span class="ms-point-success"/>
<span class="ms-point-success" />
<div class="metric-box">
<div class="value">{{ content.scenarioSuccess ? content.scenarioSuccess : 0 }}</div>
<div class="value">
{{ content.scenarioSuccess ? content.scenarioSuccess : 0 }}
</div>
<div class="name">Success</div>
</div>
<span class="ms-point-error"/>
<span class="ms-point-error" />
<div class="metric-box">
<div class="value">{{ content.scenarioError ? content.scenarioError : 0 }}</div>
<div class="value">
{{ content.scenarioError ? content.scenarioError : 0 }}
</div>
<div class="name">Error</div>
</div>
<span class="ms-point-error-code"
v-if="content.scenarioErrorReport > 0 || content.scenarioStepErrorReport > 0 "/>
<div class="metric-box" v-if="content.scenarioErrorReport > 0 || content.scenarioStepErrorReport > 0 ">
<div class="value">{{ content.scenarioErrorReport ? content.scenarioErrorReport : 0 }}</div>
<span
class="ms-point-error-code"
v-if="
content.scenarioErrorReport > 0 ||
content.scenarioStepErrorReport > 0
"
/>
<div
class="metric-box"
v-if="
content.scenarioErrorReport > 0 ||
content.scenarioStepErrorReport > 0
"
>
<div class="value">
{{
content.scenarioErrorReport ? content.scenarioErrorReport : 0
}}
</div>
<div class="name">FakeError</div>
</div>
<span v-show="showUnExecuteReport" class="ms-point-unexecute"/>
<span v-show="showUnExecuteReport" class="ms-point-unexecute" />
<div v-show="showUnExecuteReport" class="metric-box">
<div class="value">{{ content.scenarioUnExecute ? content.scenarioUnExecute : 0 }}</div>
<div class="value">
{{ content.scenarioUnExecute ? content.scenarioUnExecute : 0 }}
</div>
<div class="name">Pending</div>
</div>
</el-row>
<el-divider v-if="report.reportType !== 'API_INTEGRATED'"/>
<el-divider v-if="report.reportType !== 'API_INTEGRATED'" />
<el-row type="flex" justify="center" align="middle">
<el-row type="flex" justify="center" align="middle">
<div class="metric-box">
<div class="value">{{ content.scenarioStepTotal ? content.scenarioStepTotal : 0 }}</div>
<div class="value">
{{ content.scenarioStepTotal ? content.scenarioStepTotal : 0 }}
</div>
<div class="name" v-if="report.reportType === 'API_INTEGRATED'">
{{ $t('api_test.definition.request.case') }}
</div>
<div class="name" v-else>{{ $t('test_track.plan_view.step') }}</div>
<div class="name" v-else>
{{ $t('test_track.plan_view.step') }}
</div>
</div>
<span class="ms-point-success"/>
<span class="ms-point-success" />
<div class="metric-box">
<div class="value">{{ content.scenarioStepSuccess ? content.scenarioStepSuccess : 0 }}</div>
<div class="value">
{{
content.scenarioStepSuccess ? content.scenarioStepSuccess : 0
}}
</div>
<div class="name">Success</div>
</div>
<span class="ms-point-error"/>
<span class="ms-point-error" />
<div class="metric-box">
<div class="value">{{ content.scenarioStepError ? content.scenarioStepError : 0 }}</div>
<div class="value">
{{ content.scenarioStepError ? content.scenarioStepError : 0 }}
</div>
<div class="name">Error</div>
</div>
<span class="ms-point-error-code"
v-if="content.scenarioErrorReport > 0 || content.scenarioStepErrorReport > 0 "/>
<div class="metric-box" v-if="content.scenarioErrorReport > 0 || content.scenarioStepErrorReport > 0 ">
<span
class="ms-point-error-code"
v-if="
content.scenarioErrorReport > 0 ||
content.scenarioStepErrorReport > 0
"
/>
<div
class="metric-box"
v-if="
content.scenarioErrorReport > 0 ||
content.scenarioStepErrorReport > 0
"
>
<div class="value">
{{ content.scenarioStepErrorReport ? content.scenarioStepErrorReport : 0 }}
{{
content.scenarioStepErrorReport
? content.scenarioStepErrorReport
: 0
}}
</div>
<div class="name">FakeError</div>
</div>
<span v-show="showUnExecuteReport" class="ms-point-unexecute"/>
<span v-show="showUnExecuteReport" class="ms-point-unexecute" />
<div v-show="showUnExecuteReport" class="metric-box">
<div class="value">
{{ content.scenarioStepPending ? content.scenarioStepPending : 0 }}
{{
content.scenarioStepPending ? content.scenarioStepPending : 0
}}
</div>
<div class="name">Pending</div>
</div>
@ -172,11 +257,11 @@
</template>
<script>
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
import MsChart from 'metersphere-frontend/src/components/chart/MsChart';
export default {
name: "MsMetricChart",
components: {MsChart},
name: 'MsMetricChart',
components: { MsChart },
props: {
report: Object,
content: Object,
@ -184,7 +269,7 @@ export default {
isExport: {
type: Boolean,
default: false,
}
},
},
data() {
return {
@ -196,7 +281,7 @@ export default {
scenarioSuccess: 0,
scenarioError: 0,
reqTotal: 0,
}
};
},
created() {
this.initTime();
@ -204,25 +289,25 @@ export default {
methods: {
initTime() {
this.time = this.totalTime;
this.seconds = (this.time) / 1000;
this.seconds = this.time / 1000;
if (this.seconds >= 1) {
if (this.seconds < 60) {
this.seconds = Math.round(this.seconds * 100 / 1) / 100;
this.time = this.seconds + "s"
this.seconds = Math.round((this.seconds * 100) / 1) / 100;
this.time = this.seconds + 's';
}
if (this.seconds > 60) {
this.minutes = Math.round(this.seconds / 60)
this.seconds = Math.round(this.seconds * 100 % 60) / 100;
this.time = this.minutes + "min" + this.seconds + "s"
this.minutes = Math.round(this.seconds / 60);
this.seconds = Math.round((this.seconds * 100) % 60) / 100;
this.time = this.minutes + 'min' + this.seconds + 's';
}
if (this.minutes > 60) {
this.hour = Math.round(this.minutes / 60)
this.minutes = Math.round(this.minutes % 60)
this.time = this.hour + "hour" + this.minutes + "min" + this.seconds + "s"
this.hour = Math.round(this.minutes / 60);
this.minutes = Math.round(this.minutes % 60);
this.time =
this.hour + 'hour' + this.minutes + 'min' + this.seconds + 's';
}
} else {
this.time = this.totalTime + "ms"
this.time = this.totalTime + 'ms';
}
},
},
@ -248,29 +333,31 @@ export default {
color: ['#67C23A', '#F56C6C', '#F6972A', '#9C9B9A'],
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
formatter: '{b}: {c} ({d}%)',
},
title: [{
text: this.totalCount,
subtext: this.$t('api_report.request'),
top: 'center',
left: 'center',
textStyle: {
rich: {
align: 'center',
value: {
fontSize: 32,
fontWeight: 'bold',
padding: [10, 0]
title: [
{
text: this.totalCount,
subtext: this.$t('api_report.request'),
top: 'center',
left: 'center',
textStyle: {
rich: {
align: 'center',
value: {
fontSize: 32,
fontWeight: 'bold',
padding: [10, 0],
},
name: {
fontSize: 14,
fontWeight: 'normal',
color: '#7F7F7F',
},
},
name: {
fontSize: 14,
fontWeight: 'normal',
color: '#7F7F7F',
}
}
}
}],
},
},
],
series: [
{
type: 'pie',
@ -281,38 +368,43 @@ export default {
show: false,
},
itemStyle: {
borderColor: "#FFF",
borderColor: '#FFF',
shadowColor: '#E1E1E1',
shadowBlur: 10
shadowBlur: 10,
},
labelLine: {
show: false
show: false,
},
data: [
{value: this.content.success, name: 'Success'},
{value: this.content.error, name: 'Error'},
{value: this.content.errorCode, name: 'FakeError'},
{value: this.content.unExecute, name: 'Pending'},
]
}
]
{ value: this.content.success, name: 'Success' },
{ value: this.content.error, name: 'Error' },
{ value: this.content.errorCode, name: 'FakeError' },
{ value: this.content.unExecute, name: 'Pending' },
],
},
],
};
},
fail() {
return (this.content.error / this.content.total * 100).toFixed(0) + "%";
return ((this.content.error / this.content.total) * 100).toFixed(0) + '%';
},
assertions() {
return this.content.passAssertions + " / " + this.content.totalAssertions;
return this.content.passAssertions + ' / ' + this.content.totalAssertions;
},
errorCodeAssertions() {
return this.content.errorCode + " / " + this.content.totalAssertions;
return this.content.errorCode + ' / ' + this.content.totalAssertions;
},
showUnExecuteReport() {
return (this.content.scenarioStepPending && this.content.scenarioStepPending > 0)
|| (this.content.scenarioUnExecute && this.content.scenarioUnExecute > 0) || (this.content.unExecute && this.content.unExecute > 0);
return (
(this.content.scenarioStepPending &&
this.content.scenarioStepPending > 0) ||
(this.content.scenarioUnExecute &&
this.content.scenarioUnExecute > 0) ||
(this.content.unExecute && this.content.unExecute > 0)
);
},
},
}
};
</script>
<style scoped>
@ -330,25 +422,25 @@ export default {
.metric-container .split {
margin: 20px;
height: 100px;
border-left: 1px solid #D8DBE1;
border-left: 1px solid #d8dbe1;
}
.metric-container .circle {
width: 12px;
height: 12px;
border-radius: 50%;
box-shadow: 0 0 20px 1px rgba(200, 216, 226, .42);
box-shadow: 0 0 20px 1px rgba(200, 216, 226, 0.42);
display: inline-block;
margin-right: 10px;
vertical-align: middle;
}
.metric-container .circle.success {
background-color: #67C23A;
background-color: #67c23a;
}
.metric-container .circle.fail {
background-color: #F56C6C;
background-color: #f56c6c;
}
.metric-box {
@ -366,18 +458,18 @@ export default {
.metric-box .value {
font-size: 32px;
font-weight: 600;
letter-spacing: -.5px;
letter-spacing: -0.5px;
}
.metric-time .value {
font-size: 25px;
font-weight: 400;
letter-spacing: -.5px;
letter-spacing: -0.5px;
}
.metric-box .name {
font-size: 16px;
letter-spacing: -.2px;
letter-spacing: -0.2px;
color: #404040;
}
@ -390,7 +482,7 @@ export default {
.metric-icon-box .value {
font-size: 20px;
font-weight: 600;
letter-spacing: -.4px;
letter-spacing: -0.4px;
line-height: 28px;
vertical-align: middle;
}
@ -398,12 +490,12 @@ export default {
.metric-icon-box .name {
font-size: 13px;
letter-spacing: 1px;
color: #BFBFBF;
color: #bfbfbf;
line-height: 18px;
}
.metric-icon-box .fail {
color: #F56C6C;
color: #f56c6c;
font-size: 40px;
}
@ -425,11 +517,11 @@ export default {
}
.ms-req-error {
border: 5px #F56C6C solid;
border: 5px #f56c6c solid;
}
.ms-req-success {
border: 5px #67C23A solid;
border: 5px #67c23a solid;
}
.ms-req-span {
@ -449,7 +541,7 @@ export default {
vertical-align: top;
margin-left: 20px;
margin-right: 20px;
background-color: #67C23A;
background-color: #67c23a;
}
.ms-point-error {
@ -461,7 +553,7 @@ export default {
vertical-align: top;
margin-left: 20px;
margin-right: 20px;
background-color: #F56C6C;
background-color: #f56c6c;
}
.ms-point-error-code {
@ -473,7 +565,7 @@ export default {
vertical-align: top;
margin-left: 20px;
margin-right: 20px;
background-color: #F6972A;
background-color: #f6972a;
}
.ms-point-unexecute {
@ -485,6 +577,6 @@ export default {
vertical-align: top;
margin-left: 20px;
margin-right: 20px;
background-color: #9C9B9A;
background-color: #9c9b9a;
}
</style>

View File

@ -2,89 +2,139 @@
<div class="metric-container">
<el-row type="flex">
<el-col>
<div style="font-size: 14px;color: #AAAAAA;float: left">{{$t('api_report.response_code')}} :</div>
<div style="font-size: 14px;color:#61C550;margin-top:2px;margin-left:10px;float: left">{{request.responseResult.responseCode ? request.responseResult.responseCode :'0'}}</div>
<div style="font-size: 14px; color: #aaaaaa; float: left">
{{ $t('api_report.response_code') }} :
</div>
<div
style="
font-size: 14px;
color: #61c550;
margin-top: 2px;
margin-left: 10px;
float: left;
"
>
{{
request.responseResult.responseCode
? request.responseResult.responseCode
: '0'
}}
</div>
</el-col>
<el-col>
<div style="font-size: 14px;color: #AAAAAA;float: left">{{$t('api_report.response_time')}} :</div>
<div style="font-size: 14px;color:#61C550;margin-top:2px;margin-left:10px;float: left">{{request.responseResult.responseTime?request.responseResult.responseTime:0}} ms</div>
<div style="font-size: 14px; color: #aaaaaa; float: left">
{{ $t('api_report.response_time') }} :
</div>
<div
style="
font-size: 14px;
color: #61c550;
margin-top: 2px;
margin-left: 10px;
float: left;
"
>
{{
request.responseResult.responseTime
? request.responseResult.responseTime
: 0
}}
ms
</div>
</el-col>
<el-col>
<div style="font-size: 14px;color: #AAAAAA;float: left">{{$t('api_report.response_size')}} :</div>
<div style="font-size: 14px;color:#61C550; margin-top:2px;margin-left:10px;float: left">{{request.responseResult.responseSize?request.responseResult.responseSize:0}} bytes</div>
<div style="font-size: 14px; color: #aaaaaa; float: left">
{{ $t('api_report.response_size') }} :
</div>
<div
style="
font-size: 14px;
color: #61c550;
margin-top: 2px;
margin-left: 10px;
float: left;
"
>
{{
request.responseResult.responseSize
? request.responseResult.responseSize
: 0
}}
bytes
</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "MsRequestMetric",
export default {
name: 'MsRequestMetric',
props: {
request: Object
props: {
request: Object,
},
computed: {
error() {
return this.request.responseResult.responseCode >= 400;
},
computed: {
error() {
return this.request.responseResult.responseCode >= 400;
}
}
}
},
};
</script>
<style scoped>
.metric-container {
padding: 5px 10px;
}
.metric-container {
padding: 5px 10px;
}
.metric {
padding: 20px;
border: 1px solid #EBEEF5;
min-width: 120px;
height: 114px;
}
.metric {
padding: 20px;
border: 1px solid #ebeef5;
min-width: 120px;
height: 114px;
}
.metric + .metric {
margin-left: 20px;
}
.metric + .metric {
margin-left: 20px;
}
.metric .value {
font-size: 16px;
font-weight: 500;
word-break: break-all;
}
.metric .value {
font-size: 16px;
font-weight: 500;
word-break: break-all;
}
.metric .name {
color: #404040;
opacity: 0.5;
padding: 5px 0;
}
.metric .name {
color: #404040;
opacity: 0.5;
padding: 5px 0;
}
.metric.horizontal {
width: 100%;
}
.metric.horizontal {
width: 100%;
}
.metric .code {
min-width: 120px;
}
.metric .code {
min-width: 120px;
}
.metric .code .value {
color: #67C23A;
}
.metric .code .value {
color: #67c23a;
}
.metric .code .value.error {
color: #F56C6C;
}
.metric .code .value.error {
color: #f56c6c;
}
.metric .split {
height: 114px;
border-left: 1px solid #EBEEF5;
margin-right: 20px;
}
.metric .split {
height: 114px;
border-left: 1px solid #ebeef5;
margin-right: 20px;
}
.metric .message {
max-height: 114px;
overflow-y: auto;
}
.metric .message {
max-height: 114px;
overflow-y: auto;
}
</style>

View File

@ -3,14 +3,27 @@
<div class="request-result">
<div @click="active">
<el-row :gutter="18" type="flex" align="middle" class="info">
<el-col class="ms-req-name-col" :span="18" v-if="indexNumber!=undefined">
<el-col
class="ms-req-name-col"
:span="18"
v-if="indexNumber != undefined"
>
<el-tooltip :content="getName(request.name)" placement="top">
<span class="method ms-req-name">
<div class="el-step__icon is-text ms-api-col-create">
<div class="el-step__icon-inner"> {{ indexNumber }}</div>
<div class="el-step__icon-inner">{{ indexNumber }}</div>
</div>
<i class="icon el-icon-arrow-right" :class="{'is-active': showActive}" @click="active" @click.stop/>
<span class="report-label-req" @click="isLink" v-if="redirect && resourceId">
<i
class="icon el-icon-arrow-right"
:class="{ 'is-active': showActive }"
@click="active"
@click.stop
/>
<span
class="report-label-req"
@click="isLink"
v-if="redirect && resourceId"
>
{{ request.name }}
</span>
<span v-else>{{ getName(request.name) }}</span>
@ -19,36 +32,72 @@
</el-col>
<!-- 误报 / 异常状态显示 -->
<el-col :span="3">
<el-tooltip effect="dark" v-if="baseErrorCode && baseErrorCode !==''" :content="baseErrorCode"
style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" placement="bottom"
:open-delay="800">
<div :style="{color: statusColor(totalStatus ? totalStatus : request.status)}">
<el-tooltip
effect="dark"
v-if="baseErrorCode && baseErrorCode !== ''"
:content="baseErrorCode"
style="
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
placement="bottom"
:open-delay="800"
>
<div
:style="{
color: statusColor(
totalStatus ? totalStatus : request.status
),
}"
>
{{ baseErrorCode }}
</div>
</el-tooltip>
</el-col>
<!-- 请求返回状态 -->
<el-col :span="6">
<el-tooltip effect="dark" :content="request.responseResult.responseCode"
style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" placement="bottom"
:open-delay="800">
<div :style="{color: statusColor(totalStatus ? totalStatus : request.status)}">
<el-tooltip
effect="dark"
:content="request.responseResult.responseCode"
style="
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
placement="bottom"
:open-delay="800"
>
<div
:style="{
color: statusColor(
totalStatus ? totalStatus : request.status
),
}"
>
{{ request.responseResult.responseCode }}
</div>
</el-tooltip>
</el-col>
<!-- 请求响应时间 -->
<el-col :span="3">
<div :style="{color: statusColor(totalStatus ? totalStatus : request.status)}">
<div
:style="{
color: statusColor(totalStatus ? totalStatus : request.status),
}"
>
{{ request.responseResult.responseTime }} ms
</div>
</el-col>
<el-col :span="2">
<el-tag v-if="request.testing" class="ms-test-running" size="mini">
<i class="el-icon-loading" style="font-size: 16px"/>
<i class="el-icon-loading" style="font-size: 16px" />
{{ $t('commons.testing') }}
</el-tag>
<ms-api-report-status :status="totalStatus || request.status" v-else/>
<ms-api-report-status
:status="totalStatus || request.status"
v-else
/>
</el-col>
</el-row>
</div>
@ -61,7 +110,8 @@
:request-type="requestType"
:request="requestInfo"
:console="console"
v-if="showActive"/>
v-if="showActive"
/>
</div>
</el-collapse-transition>
</div>
@ -69,27 +119,28 @@
</template>
<script>
import MsRequestMetric from "./RequestMetric";
import MsAssertionResults from "./AssertionResults";
import MsRequestText from "./RequestText";
import MsResponseText from "./ResponseText";
import MsRequestResultTail from "./RequestResultTail";
import {getCurrentByResourceId} from "../../../../api/user";
import {getShareContent} from "../../../../api/share";
import {getScenarioReportStepDetail} from "../../../../api/scenario-report";
import MsApiReportStatus from "../ApiReportStatus";
import MsRequestMetric from './RequestMetric';
import MsAssertionResults from './AssertionResults';
import MsRequestText from './RequestText';
import MsResponseText from './ResponseText';
import MsRequestResultTail from './RequestResultTail';
import { getCurrentByResourceId } from '../../../../api/user';
import { getShareContent } from '../../../../api/share';
import { getScenarioReportStepDetail } from '../../../../api/scenario-report';
import MsApiReportStatus from '../ApiReportStatus';
const {getReportStatusColor} = require("../../../../business/commons/js/commons");
const {
getReportStatusColor,
} = require('../../../../business/commons/js/commons');
export default {
name: "MsRequestResult",
name: 'MsRequestResult',
components: {
MsApiReportStatus,
MsResponseText,
MsRequestText,
MsAssertionResults,
MsRequestMetric,
MsRequestResultTail
MsRequestResultTail,
},
props: {
request: Object,
@ -102,11 +153,11 @@ export default {
redirect: Boolean,
errorCode: {
type: String,
default: ""
default: '',
},
isActive: {
type: Boolean,
default: false
default: false,
},
isShare: Boolean,
shareId: String,
@ -117,12 +168,12 @@ export default {
},
data() {
return {
requestType: "",
requestType: '',
color: {
type: String,
default() {
return "#B8741A";
}
return '#B8741A';
},
},
requestInfo: {
loading: true,
@ -130,15 +181,15 @@ export default {
responseResult: {},
subRequestResults: [],
},
baseErrorCode: "",
baseErrorCode: '',
backgroundColor: {
type: String,
default() {
return "#F9F1EA";
}
return '#F9F1EA';
},
},
showActive: false,
}
};
},
watch: {
isActive() {
@ -153,21 +204,24 @@ export default {
handler(n) {
if (this.request.errorCode) {
this.baseErrorCode = this.request.errorCode;
} else if (this.request.attachInfoMap && this.request.attachInfoMap.FAKE_ERROR) {
if (this.request.attachInfoMap.FAKE_ERROR !== "") {
} else if (
this.request.attachInfoMap &&
this.request.attachInfoMap.FAKE_ERROR
) {
if (this.request.attachInfoMap.FAKE_ERROR !== '') {
this.baseErrorCode = this.request.attachInfoMap.FAKE_ERROR;
}
}
},
}
},
},
methods: {
statusColor(status) {
return getReportStatusColor(status);
},
isLink() {
let uri = "/#/api/definition?caseId=" + this.resourceId;
this.clickResource(uri)
let uri = '/#/api/definition?caseId=' + this.resourceId;
this.clickResource(uri);
},
clickResource(uri) {
getCurrentByResourceId(this.resourceId).then(() => {
@ -175,11 +229,11 @@ export default {
});
},
toPage(uri) {
let id = "new_a";
let a = document.createElement("a");
a.setAttribute("href", uri);
a.setAttribute("target", "_blank");
a.setAttribute("id", id);
let id = 'new_a';
let a = document.createElement('a');
a.setAttribute('href', uri);
a.setAttribute('target', '_blank');
a.setAttribute('id', id);
document.body.appendChild(a);
a.click();
@ -187,23 +241,26 @@ export default {
element.parentNode.removeChild(element);
},
loadRequestInfoExpand() {
if (!this.request.responseResult || this.request.responseResult.body === null || this.request.responseResult.body === undefined) {
if (
!this.request.responseResult ||
this.request.responseResult.body === null ||
this.request.responseResult.body === undefined
) {
if (this.isShare) {
getShareContent(this.shareId, this.stepId).then(response => {
getShareContent(this.shareId, this.stepId).then((response) => {
this.requestInfo = response.data;
this.$nextTick(() => {
this.requestInfo.loading = false;
});
});
} else {
getScenarioReportStepDetail(this.stepId).then(response => {
getScenarioReportStepDetail(this.stepId).then((response) => {
this.requestInfo = response.data;
this.$nextTick(() => {
this.requestInfo.loading = false;
});
});
}
} else {
this.requestInfo = this.request;
}
@ -219,30 +276,30 @@ export default {
}
},
getName(name) {
if (name && name.indexOf("<->") !== -1) {
return name.split("<->")[0];
if (name && name.indexOf('<->') !== -1) {
return name.split('<->')[0];
}
if (name && name.indexOf("^@~@^") !== -1) {
let arr = name.split("^@~@^");
if (name && name.indexOf('^@~@^') !== -1) {
let arr = name.split('^@~@^');
let value = arr[arr.length - 1];
if (value.indexOf("UUID=") !== -1) {
return value.split("UUID=")[0];
if (value.indexOf('UUID=') !== -1) {
return value.split('UUID=')[0];
}
if (value && value.startsWith("UUID=")) {
return "";
if (value && value.startsWith('UUID=')) {
return '';
}
if (value && value.indexOf("<->") !== -1) {
return value.split("<->")[0];
if (value && value.indexOf('<->') !== -1) {
return value.split('<->')[0];
}
return value;
}
if (name && name.startsWith("UUID=")) {
return "";
if (name && name.startsWith('UUID=')) {
return '';
}
return name;
}
},
},
}
};
</script>
<style scoped>
@ -257,7 +314,7 @@ export default {
}
.request-result .method {
color: #1E90FF;
color: #1e90ff;
font-size: 14px;
font-weight: 500;
line-height: 35px;
@ -289,11 +346,11 @@ export default {
}
.sub-result .info {
background-color: #FFF;
background-color: #fff;
}
.sub-result .method {
border-left: 5px solid #1E90FF;
border-left: 5px solid #1e90ff;
padding-left: 20px;
}
@ -302,7 +359,7 @@ export default {
}
.sub-result:last-child {
border-bottom: 1px solid #EBEEF5;
border-bottom: 1px solid #ebeef5;
}
.ms-test-running {
@ -310,21 +367,21 @@ export default {
}
.ms-test-error_code {
color: #F6972A;
background-color: #FDF5EA;
border-color: #FDF5EA;
color: #f6972a;
background-color: #fdf5ea;
border-color: #fdf5ea;
}
.ms-api-col {
background-color: #EFF0F0;
border-color: #EFF0F0;
background-color: #eff0f0;
border-color: #eff0f0;
margin-right: 10px;
font-size: 12px;
color: #64666A;
color: #64666a;
}
.ms-api-col-create {
background-color: #EBF2F2;
background-color: #ebf2f2;
border-color: #008080;
margin-right: 10px;
font-size: 12px;

View File

@ -8,14 +8,23 @@
</div>
</el-col>
<el-col :span="8">
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
<el-tooltip
effect="dark"
:content="request.url"
placement="bottom"
:open-delay="800"
>
<div class="url">{{ request.url }}</div>
</el-tooltip>
</el-col>
<el-col :span="8">
<div class="url">
{{ $t('api_report.start_time') }}{{ request.startTime | datetimeFormat(true) }}
{{ $t('report.test_end_time') }}{{ request.endTime | datetimeFormat(true) }}
{{ $t('api_report.start_time') }}{{
request.startTime | datetimeFormat(true)
}}
{{ $t('report.test_end_time') }}{{
request.endTime | datetimeFormat(true)
}}
</div>
</el-col>
</el-row>
@ -24,15 +33,31 @@
<div v-show="isActive">
<el-tabs v-model="activeName" v-show="isActive" v-if="hasSub">
<el-tab-pane :label="$t('api_report.sub_result')" name="sub">
<ms-request-sub-result class="sub-result" v-for="(sub, index) in request.subRequestResults"
:key="index" :indexNumber="index" :request="sub"/>
<ms-request-sub-result
class="sub-result"
v-for="(sub, index) in request.subRequestResults"
:key="index"
:indexNumber="index"
:request="sub"
/>
</el-tab-pane>
<el-tab-pane :label="$t('api_report.request_result')" name="result">
<ms-response-text :console="console" :request-type="requestType" :response="request.responseResult" :request="request"/>
<ms-response-text
:console="console"
:request-type="requestType"
:response="request.responseResult"
:request="request"
/>
</el-tab-pane>
</el-tabs>
<div v-else>
<ms-response-text :console="console" :request-type="requestType" v-if="isCodeEditAlive" :response="request.responseResult" :request="request"/>
<ms-response-text
:console="console"
:request-type="requestType"
v-if="isCodeEditAlive"
:response="request.responseResult"
:request="request"
/>
</div>
</div>
</el-collapse-transition>
@ -40,16 +65,23 @@
</template>
<script>
import MsRequestMetric from "./RequestMetric";
import MsAssertionResults from "./AssertionResults";
import MsRequestText from "./RequestText";
import MsResponseText from "./ResponseText";
import MsRequestResult from "./RequestResult";
import MsRequestSubResult from "./RequestSubResult";
import MsRequestMetric from './RequestMetric';
import MsAssertionResults from './AssertionResults';
import MsRequestText from './RequestText';
import MsResponseText from './ResponseText';
import MsRequestResult from './RequestResult';
import MsRequestSubResult from './RequestSubResult';
export default {
name: "MsRequestResultTail",
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult, MsRequestSubResult},
name: 'MsRequestResultTail',
components: {
MsResponseText,
MsRequestText,
MsAssertionResults,
MsRequestMetric,
MsRequestResult,
MsRequestSubResult,
},
props: {
request: Object,
scenarioName: String,
@ -60,9 +92,9 @@ export default {
data() {
return {
isActive: true,
activeName: "sub",
isCodeEditAlive: true
}
activeName: 'sub',
isCodeEditAlive: true,
};
},
methods: {
@ -72,24 +104,24 @@ export default {
reload() {
this.isCodeEditAlive = false;
this.$nextTick(() => (this.isCodeEditAlive = true));
}
},
},
watch: {
'request.responseResult'() {
this.reload();
}
},
},
computed: {
assertion() {
return this.request.passAssertions + " / " + this.request.totalAssertions;
return this.request.passAssertions + ' / ' + this.request.totalAssertions;
},
hasSub() {
return this.request.subRequestResults.length > 0;
},
}
}
},
};
</script>
<style scoped>
@ -100,13 +132,13 @@ export default {
}
.request-result .info {
background-color: #F9F9F9;
background-color: #f9f9f9;
margin-left: 20px;
cursor: pointer;
}
.request-result .method {
color: #1E90FF;
color: #1e90ff;
font-size: 14px;
font-weight: 500;
line-height: 40px;
@ -134,20 +166,19 @@ export default {
}
.sub-result .info {
background-color: #FFF;
background-color: #fff;
}
.sub-result .method {
border-left: 5px solid #1E90FF;
border-left: 5px solid #1e90ff;
padding-left: 20px;
}
.sub-result:last-child {
border-bottom: 1px solid #EBEEF5;
border-bottom: 1px solid #ebeef5;
}
.request-result .icon.is-active {
transform: rotate(90deg);
}
</style>

View File

@ -3,17 +3,30 @@
<p class="el-divider--horizontal"></p>
<div @click="active">
<el-row :gutter="10" type="flex" align="middle" class="info">
<el-col :span="6" v-if="indexNumber!=undefined">
<el-col :span="6" v-if="indexNumber != undefined">
<div class="method">
<div style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
<div class="el-step__icon is-text ms-api-col" v-if="indexNumber%2 ==0">
<div class="el-step__icon-inner"> {{ indexNumber + 1 }}</div>
<div
style="
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
>
<div
class="el-step__icon is-text ms-api-col"
v-if="indexNumber % 2 == 0"
>
<div class="el-step__icon-inner">{{ indexNumber + 1 }}</div>
</div>
<div class="el-step__icon is-text ms-api-col-create" v-else>
<div class="el-step__icon-inner"> {{ indexNumber + 1 }}</div>
<div class="el-step__icon-inner">{{ indexNumber + 1 }}</div>
</div>
<i class="icon el-icon-arrow-right" :class="{'is-active': isActive}" @click="active" @click.stop/>
<i
class="icon el-icon-arrow-right"
:class="{ 'is-active': isActive }"
@click="active"
@click.stop
/>
{{ getName(request.name) }}
</div>
</div>
@ -25,7 +38,16 @@
</el-col>
<el-col :span="6">
<div class="url">
<el-tooltip :content="request.url " style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" placement="bottom" :open-delay="800">
<el-tooltip
:content="request.url"
style="
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
placement="bottom"
:open-delay="800"
>
<div>
{{ request.url }}
</div>
@ -33,8 +55,15 @@
</div>
</el-col>
<el-col :span="5">
<el-tooltip effect="dark" :content="request.responseResult.responseCode" placement="bottom" :open-delay="800">
<div class="url" style="color: #5daf34">{{ request.responseResult.responseCode }}</div>
<el-tooltip
effect="dark"
:content="request.responseResult.responseCode"
placement="bottom"
:open-delay="800"
>
<div class="url" style="color: #5daf34">
{{ request.responseResult.responseCode }}
</div>
</el-tooltip>
</el-col>
<el-col :span="3">
@ -46,34 +75,39 @@
<el-tag size="mini" type="success" v-if="request.success">
Success
</el-tag>
<el-tag size="mini" type="danger" v-else>
Error
</el-tag>
<el-tag size="mini" type="danger" v-else> Error </el-tag>
</div>
</el-col>
</el-row>
</div>
<el-collapse-transition>
<div v-show="isActive" style="width: 99%">
<ms-request-sub-result-tail :scenario-name="scenarioName"
:request-type="requestType" v-if="isActive"
:request="request"/>
<ms-request-sub-result-tail
:scenario-name="scenarioName"
:request-type="requestType"
v-if="isActive"
:request="request"
/>
</div>
</el-collapse-transition>
</div>
</template>
<script>
import MsRequestMetric from "./RequestMetric";
import MsAssertionResults from "./AssertionResults";
import MsRequestText from "./RequestText";
import MsResponseText from "./ResponseText";
import MsRequestSubResultTail from "./RequestSubResultTail";
import MsRequestMetric from './RequestMetric';
import MsAssertionResults from './AssertionResults';
import MsRequestText from './RequestText';
import MsResponseText from './ResponseText';
import MsRequestSubResultTail from './RequestSubResultTail';
export default {
name: "MsRequestSubResult",
name: 'MsRequestSubResult',
components: {
MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestSubResultTail
MsResponseText,
MsRequestText,
MsAssertionResults,
MsRequestMetric,
MsRequestSubResultTail,
},
props: {
request: Object,
@ -81,37 +115,37 @@ export default {
indexNumber: Number,
},
data() {
return {isActive: false, requestType: undefined,}
return { isActive: false, requestType: undefined };
},
methods: {
active() {
this.isActive = !this.isActive;
},
getName(name) {
if (name && name.indexOf("<->") !== -1) {
return name.split("<->")[0];
if (name && name.indexOf('<->') !== -1) {
return name.split('<->')[0];
}
if (name && name.indexOf("^@~@^") !== -1) {
let arr = name.split("^@~@^");
if (name && name.indexOf('^@~@^') !== -1) {
let arr = name.split('^@~@^');
let value = arr[arr.length - 1];
if (value.indexOf("UUID=") !== -1) {
return value.split("UUID=")[0];
if (value.indexOf('UUID=') !== -1) {
return value.split('UUID=')[0];
}
if (value && value.startsWith("UUID=")) {
return "";
if (value && value.startsWith('UUID=')) {
return '';
}
if (value && value.indexOf("<->") !== -1) {
return value.split("<->")[0];
if (value && value.indexOf('<->') !== -1) {
return value.split('<->')[0];
}
return value;
}
if (name && name.startsWith("UUID=")) {
return "";
if (name && name.startsWith('UUID=')) {
return '';
}
return name;
}
},
},
}
};
</script>
<style scoped>
@ -127,7 +161,7 @@ export default {
}
.request-result .method {
color: #1E90FF;
color: #1e90ff;
font-size: 14px;
font-weight: 500;
line-height: 40px;
@ -155,28 +189,28 @@ export default {
}
.sub-result .info {
background-color: #FFF;
background-color: #fff;
}
.sub-result .method {
border-left: 5px solid #1E90FF;
border-left: 5px solid #1e90ff;
padding-left: 20px;
}
.sub-result:last-child {
border-bottom: 1px solid #EBEEF5;
border-bottom: 1px solid #ebeef5;
}
.ms-api-col {
background-color: #EFF0F0;
border-color: #EFF0F0;
background-color: #eff0f0;
border-color: #eff0f0;
margin-right: 10px;
font-size: 12px;
color: #64666A;
color: #64666a;
}
.ms-api-col-create {
background-color: #EBF2F2;
background-color: #ebf2f2;
border-color: #008080;
margin-right: 10px;
font-size: 12px;

View File

@ -4,135 +4,154 @@
<el-row :gutter="8" type="flex" align="middle" class="info">
<el-col :span="2">
<div class="method">
{{request.method}}
{{ request.method }}
</div>
</el-col>
<el-col :span="8">
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
<div class="url">{{request.url}}</div>
<el-tooltip
effect="dark"
:content="request.url"
placement="bottom"
:open-delay="800"
>
<div class="url">{{ request.url }}</div>
</el-tooltip>
</el-col>
<el-col :span="8">
<div class="url"> {{$t('api_report.start_time')}}{{request.startTime | datetimeFormat(true) }}
<div class="url">
{{ $t('api_report.start_time') }}{{
request.startTime | datetimeFormat(true)
}}
</div>
</el-col>
</el-row>
</div>
<el-collapse-transition>
<div v-show="isActive">
<ms-response-text :request-type="requestType" v-if="isCodeEditAlive" :response="request.responseResult" :request="request"/>
<ms-response-text
:request-type="requestType"
v-if="isCodeEditAlive"
:response="request.responseResult"
:request="request"
/>
</div>
</el-collapse-transition>
</div>
</template>
<script>
import MsRequestMetric from "./RequestMetric";
import MsAssertionResults from "./AssertionResults";
import MsRequestText from "./RequestText";
import MsResponseText from "./ResponseText";
import MsRequestResult from "./RequestResult";
import MsRequestSubResult from "./RequestSubResult";
import MsRequestMetric from './RequestMetric';
import MsAssertionResults from './AssertionResults';
import MsRequestText from './RequestText';
import MsResponseText from './ResponseText';
import MsRequestResult from './RequestResult';
import MsRequestSubResult from './RequestSubResult';
export default {
name: "MsRequestSubResultTail",
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult, MsRequestSubResult},
props: {
request: Object,
scenarioName: String,
requestType: String
},
export default {
name: 'MsRequestSubResultTail',
components: {
MsResponseText,
MsRequestText,
MsAssertionResults,
MsRequestMetric,
MsRequestResult,
MsRequestSubResult,
},
props: {
request: Object,
scenarioName: String,
requestType: String,
},
data() {
return {
isActive: true,
activeName: "sub",
isCodeEditAlive: true
}
},
data() {
return {
isActive: true,
activeName: 'sub',
isCodeEditAlive: true,
};
},
methods: {
active() {
this.isActive = !this.isActive;
},
reload() {
this.isCodeEditAlive = false;
this.$nextTick(() => (this.isCodeEditAlive = true));
}
methods: {
active() {
this.isActive = !this.isActive;
},
watch: {
'request.responseResult'() {
this.reload();
}
reload() {
this.isCodeEditAlive = false;
this.$nextTick(() => (this.isCodeEditAlive = true));
},
},
watch: {
'request.responseResult'() {
this.reload();
},
},
computed: {
assertion() {
return this.request.passAssertions + " / " + this.request.totalAssertions;
},
hasSub() {
return this.request.subRequestResults.length > 0;
},
}
}
computed: {
assertion() {
return this.request.passAssertions + ' / ' + this.request.totalAssertions;
},
hasSub() {
return this.request.subRequestResults.length > 0;
},
},
};
</script>
<style scoped>
.request-result {
width: 100%;
min-height: 40px;
padding: 2px 0;
}
.request-result {
width: 100%;
min-height: 40px;
padding: 2px 0;
}
.request-result .info {
background-color: #F9F9F9;
margin-left: 20px;
cursor: pointer;
}
.request-result .info {
background-color: #f9f9f9;
margin-left: 20px;
cursor: pointer;
}
.request-result .method {
color: #1E90FF;
font-size: 14px;
font-weight: 500;
line-height: 40px;
padding-left: 5px;
}
.request-result .method {
color: #1e90ff;
font-size: 14px;
font-weight: 500;
line-height: 40px;
padding-left: 5px;
}
.request-result .url {
color: #7f7f7f;
font-size: 12px;
font-weight: 400;
margin-top: 4px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
word-break: break-all;
}
.request-result .url {
color: #7f7f7f;
font-size: 12px;
font-weight: 400;
margin-top: 4px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
word-break: break-all;
}
.request-result .tab .el-tabs__header {
margin: 0;
}
.request-result .tab .el-tabs__header {
margin: 0;
}
.request-result .text {
height: 300px;
overflow-y: auto;
}
.request-result .text {
height: 300px;
overflow-y: auto;
}
.sub-result .info {
background-color: #FFF;
}
.sub-result .info {
background-color: #fff;
}
.sub-result .method {
border-left: 5px solid #1E90FF;
padding-left: 20px;
}
.sub-result .method {
border-left: 5px solid #1e90ff;
padding-left: 20px;
}
.sub-result:last-child {
border-bottom: 1px solid #EBEEF5;
}
.request-result .icon.is-active {
transform: rotate(90deg);
}
.sub-result:last-child {
border-bottom: 1px solid #ebeef5;
}
.request-result .icon.is-active {
transform: rotate(90deg);
}
</style>

View File

@ -1,19 +1,19 @@
<template>
<div class="text-container">
<div @click="active" class="collapse">
<i class="icon el-icon-arrow-right" :class="{'is-active': isActive}"/>
{{$t('api_report.request')}}
<i class="icon el-icon-arrow-right" :class="{ 'is-active': isActive }" />
{{ $t('api_report.request') }}
</div>
<el-collapse-transition>
<el-tabs v-model="activeName" v-show="isActive">
<el-tab-pane label="Body" name="body" class="pane">
<pre>{{request.body}}</pre>
<pre>{{ request.body }}</pre>
</el-tab-pane>
<el-tab-pane label="Headers" name="headers" class="pane">
<pre>{{request.headers}}</pre>
<pre>{{ request.headers }}</pre>
</el-tab-pane>
<el-tab-pane label="Cookies" name="cookies" class="pane">
<pre>{{request.cookies}}</pre>
<pre>{{ request.cookies }}</pre>
</el-tab-pane>
</el-tabs>
</el-collapse-transition>
@ -21,53 +21,53 @@
</template>
<script>
export default {
name: "MsRequestText",
export default {
name: 'MsRequestText',
props: {
request: Object
},
props: {
request: Object,
},
data() {
return {
isActive: true,
activeName: "body",
}
},
data() {
return {
isActive: true,
activeName: 'body',
};
},
methods: {
active() {
this.isActive = !this.isActive;
}
methods: {
active() {
this.isActive = !this.isActive;
},
}
},
};
</script>
<style scoped>
.text-container .icon {
padding: 5px;
}
.text-container .icon {
padding: 5px;
}
.text-container .collapse {
cursor: pointer;
}
.text-container .collapse {
cursor: pointer;
}
.text-container .collapse:hover {
opacity: 0.8;
}
.text-container .collapse:hover {
opacity: 0.8;
}
.text-container .icon.is-active {
transform: rotate(90deg);
}
.text-container .icon.is-active {
transform: rotate(90deg);
}
.text-container .pane {
background-color: #F9F9F9;
padding: 1px 0;
height: 250px;
overflow-y: auto;
}
.text-container .pane {
background-color: #f9f9f9;
padding: 1px 0;
height: 250px;
overflow-y: auto;
}
pre {
margin: 0;
}
pre {
margin: 0;
}
</style>

View File

@ -2,24 +2,52 @@
<div class="text-container">
<el-collapse-transition>
<el-tabs v-model="activeName" v-show="isActive">
<el-tab-pane :class="'body-pane'" :label="$t('api_test.definition.request.response_body')" name="body" class="pane">
<ms-sql-result-table v-if="isSqlType" :body="response.body"/>
<ms-code-edit v-if="!isSqlType" :mode="mode" :read-only="true" :data="response.body" :modes="modes" ref="codeEdit"/>
<el-tab-pane
:class="'body-pane'"
:label="$t('api_test.definition.request.response_body')"
name="body"
class="pane"
>
<ms-sql-result-table v-if="isSqlType" :body="response.body" />
<ms-code-edit
v-if="!isSqlType"
:mode="mode"
:read-only="true"
:data="response.body"
:modes="modes"
ref="codeEdit"
/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.response_header')" name="headers" class="pane">
<el-tab-pane
:label="$t('api_test.definition.request.response_header')"
name="headers"
class="pane"
>
<pre>{{ response.headers }}</pre>
</el-tab-pane>
<el-tab-pane :label="$t('api_report.assertions')" name="assertions" class="pane assertions">
<ms-assertion-results :assertions="response.assertions"/>
<el-tab-pane
:label="$t('api_report.assertions')"
name="assertions"
class="pane assertions"
>
<ms-assertion-results :assertions="response.assertions" />
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.extract.label')" name="label" class="pane">
<el-tab-pane
:label="$t('api_test.request.extract.label')"
name="label"
class="pane"
>
<pre>{{ response.vars }}</pre>
</el-tab-pane>
<el-tab-pane :label="$t('api_report.request_body')" name="request_body" class="pane">
<el-tab-pane
:label="$t('api_report.request_body')"
name="request_body"
class="pane"
>
<div class="ms-div">
{{ $t('api_test.request.address') }} :
<pre>{{ request.url }}</pre>
@ -36,13 +64,28 @@
Body :
<pre>{{ request.body }}</pre>
</div>
</el-tab-pane>
<el-tab-pane v-if="activeName == 'body'" :disabled="true" name="mode" class="pane assertions">
<el-tab-pane
v-if="activeName == 'body'"
:disabled="true"
name="mode"
class="pane assertions"
>
<template v-slot:label>
<ms-dropdown v-if="request.method==='SQL'" :commands="sqlModes" :default-command="mode" @command="sqlModeChange"/>
<ms-dropdown v-else :commands="modes" :default-command="mode" @command="modeChange" ref="modeDropdown"/>
<ms-dropdown
v-if="request.method === 'SQL'"
:commands="sqlModes"
:default-command="mode"
@command="sqlModeChange"
/>
<ms-dropdown
v-else
:commands="modes"
:default-command="mode"
@command="modeChange"
ref="modeDropdown"
/>
</template>
</el-tab-pane>
</el-tabs>
@ -51,14 +94,19 @@
</template>
<script>
import MsAssertionResults from "./AssertionResults";
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
import MsDropdown from "../../../../business/commons/MsDropdown";
import {BODY_FORMAT, RequestFactory, Request, SqlRequest} from "../../../../business/definition/model/ApiTestModel";
import MsSqlResultTable from "./SqlResultTable";
import MsAssertionResults from './AssertionResults';
import MsCodeEdit from 'metersphere-frontend/src/components/MsCodeEdit';
import MsDropdown from '../../../../business/commons/MsDropdown';
import {
BODY_FORMAT,
RequestFactory,
Request,
SqlRequest,
} from '../../../../business/definition/model/ApiTestModel';
import MsSqlResultTable from './SqlResultTable';
export default {
name: "MsResponseText",
name: 'MsResponseText',
components: {
MsSqlResultTable,
@ -77,11 +125,11 @@ export default {
data() {
return {
isActive: true,
activeName: "body",
activeName: 'body',
modes: ['text', 'json', 'xml', 'html'],
sqlModes: ['text', 'table'],
mode: BODY_FORMAT.TEXT
}
mode: BODY_FORMAT.TEXT,
};
},
methods: {
@ -93,28 +141,32 @@ export default {
},
sqlModeChange(mode) {
this.mode = mode;
}
},
},
mounted() {
if (!this.response.headers) {
return;
}
if (this.response.headers.indexOf("Content-Type: application/json") > 0) {
if (this.response.headers.indexOf('Content-Type: application/json') > 0) {
this.mode = BODY_FORMAT.JSON;
}
},
computed: {
isSqlType() {
return ((this.requestType === RequestFactory.TYPES.SQL || this.request.method === RequestFactory.TYPES.SQL) && this.response.responseCode === '200' && this.mode === 'table');
}
}
}
return (
(this.requestType === RequestFactory.TYPES.SQL ||
this.request.method === RequestFactory.TYPES.SQL) &&
this.response.responseCode === '200' &&
this.mode === 'table'
);
},
},
};
</script>
<style scoped>
.body-pane {
padding: 10px !important;
background: white !important;
@ -137,7 +189,7 @@ export default {
}
.text-container .pane {
background-color: #F5F5F5;
background-color: #f5f5f5;
padding: 1px 0;
height: 250px;
overflow-y: auto;

View File

@ -1,7 +1,13 @@
<template>
<div class="scenario-result">
<div v-if="(node.children && node.children.length >0) || node.unsolicited
|| (node.type && this.stepFilter.get('AllSamplerProxy').indexOf(node.type) === -1)">
<div
v-if="
(node.children && node.children.length > 0) ||
node.unsolicited ||
(node.type &&
this.stepFilter.get('AllSamplerProxy').indexOf(node.type) === -1)
"
>
<el-card class="ms-card">
<div class="el-step__icon is-text ms-api-col">
<div class="el-step__icon-inner">
@ -9,7 +15,11 @@
</div>
</div>
<el-tooltip effect="dark" :content="node.label" placement="top">
<el-link v-if="node.redirect" class="report-label-head" @click="isLink">
<el-link
v-if="node.redirect"
class="report-label-head"
@click="isLink"
>
{{ getLabel(node.label) }}
</el-link>
<span v-else>{{ getLabel(node.label) }}</span>
@ -37,14 +47,14 @@
</template>
<script>
import MsRequestResult from "./RequestResult";
import {STEP} from "../../../../business/automation/scenario/Setting";
import {getCurrentByResourceId} from "../../../../api/user";
import MsRequestResult from './RequestResult';
import { STEP } from '../../../../business/automation/scenario/Setting';
import { getCurrentByResourceId } from '../../../../api/user';
export default {
name: "MsScenarioResult",
name: 'MsScenarioResult',
components: {
MsRequestResult
MsRequestResult,
},
props: {
scenario: Object,
@ -56,25 +66,25 @@ export default {
},
data() {
return {
stepFilter: new STEP,
}
stepFilter: new STEP(),
};
},
methods: {
getLabel(label) {
switch (label) {
case "ConstantTimer":
return "等待控制器";
case "LoopController":
return "循环控制器";
case "Assertion":
return "场景断言";
case 'ConstantTimer':
return '等待控制器';
case 'LoopController':
return '循环控制器';
case 'Assertion':
return '场景断言';
default:
return label;
}
},
isLink() {
let uri = "/#/api/automation?resourceId=" + this.node.resourceId;
this.clickResource(uri)
let uri = '/#/api/automation?resourceId=' + this.node.resourceId;
this.clickResource(uri);
},
clickResource(uri) {
getCurrentByResourceId(this.node.resourceId).then(() => {
@ -82,11 +92,11 @@ export default {
});
},
toPage(uri) {
let id = "new_a";
let a = document.createElement("a");
a.setAttribute("href", uri);
a.setAttribute("target", "_blank");
a.setAttribute("id", id);
let id = 'new_a';
let a = document.createElement('a');
a.setAttribute('href', uri);
a.setAttribute('target', '_blank');
a.setAttribute('id', id);
document.body.appendChild(a);
a.click();
@ -97,19 +107,21 @@ export default {
this.isActive = !this.isActive;
},
requestResult(requestResult) {
this.$emit("requestResult", requestResult);
}
this.$emit('requestResult', requestResult);
},
},
computed: {
assertion() {
return this.scenario.passAssertions + " / " + this.scenario.totalAssertions;
return (
this.scenario.passAssertions + ' / ' + this.scenario.totalAssertions
);
},
success() {
return this.scenario.error === 0;
}
}
}
},
},
};
</script>
<style scoped>
@ -119,7 +131,7 @@ export default {
}
.scenario-result + .scenario-result {
border-top: 1px solid #DCDFE6;
border-top: 1px solid #dcdfe6;
}
.ms-card :deep(.el-card__body) {
@ -143,15 +155,15 @@ export default {
}
.ms-api-col {
background-color: #EFF0F0;
border-color: #EFF0F0;
background-color: #eff0f0;
border-color: #eff0f0;
margin-right: 10px;
font-size: 12px;
color: #64666A;
color: #64666a;
}
.ms-card .ms-api-col-create {
background-color: #EBF2F2;
background-color: #ebf2f2;
border-color: #008080;
margin-right: 10px;
font-size: 12px;
@ -161,7 +173,8 @@ export default {
.report-label-head {
border-bottom: 1px solid #303133;
color: #303133;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
Arial, sans-serif;
font-size: 13px;
}

View File

@ -1,33 +1,62 @@
<template>
<el-card class="scenario-results">
<div v-if="errorReport > 0">
<el-tooltip :content="$t('api_test.automation.open_expansion')" placement="top" effect="light">
<i class="el-icon-circle-plus-outline ms-open-btn ms-open-btn-left" v-prevent-re-click @click="openExpansion"/>
<el-tooltip
:content="$t('api_test.automation.open_expansion')"
placement="top"
effect="light"
>
<i
class="el-icon-circle-plus-outline ms-open-btn ms-open-btn-left"
v-prevent-re-click
@click="openExpansion"
/>
</el-tooltip>
<el-tooltip :content="$t('api_test.automation.close_expansion')" placement="top" effect="light">
<i class="el-icon-remove-outline ms-open-btn" size="mini" @click="closeExpansion"/>
<el-tooltip
:content="$t('api_test.automation.close_expansion')"
placement="top"
effect="light"
>
<i
class="el-icon-remove-outline ms-open-btn"
size="mini"
@click="closeExpansion"
/>
</el-tooltip>
</div>
<el-tree :data="treeData"
:expand-on-click-node="false"
:default-expand-all="defaultExpand"
:filter-node-method="filterNode"
highlight-current
class="ms-tree ms-report-tree" ref="resultsTree">
<span slot-scope="{ node, data}" style="width: 99%" @click="nodeClick(node)">
<ms-scenario-result :node="data" :console="console" v-on:requestResult="requestResult"
:isActive="isActive" :is-share="isShare" :share-id="shareId"/>
</span>
<el-tree
:data="treeData"
:expand-on-click-node="false"
:default-expand-all="defaultExpand"
:filter-node-method="filterNode"
highlight-current
class="ms-tree ms-report-tree"
ref="resultsTree"
>
<span
slot-scope="{ node, data }"
style="width: 99%"
@click="nodeClick(node)"
>
<ms-scenario-result
:node="data"
:console="console"
v-on:requestResult="requestResult"
:isActive="isActive"
:is-share="isShare"
:share-id="shareId"
/>
</span>
</el-tree>
</el-card>
</template>
<script>
import MsScenarioResult from "./ScenarioResult";
import MsScenarioResult from './ScenarioResult';
export default {
name: "MsScenarioResults",
components: {MsScenarioResult},
name: 'MsScenarioResults',
components: { MsScenarioResult },
props: {
scenarios: Array,
treeData: Array,
@ -43,8 +72,8 @@ export default {
},
data() {
return {
isActive: false
}
isActive: false,
};
},
created() {
if (this.$refs.resultsTree && this.$refs.resultsTree.root) {
@ -53,7 +82,11 @@ export default {
},
computed: {
isUi() {
return this.report && this.report.reportType && this.report.reportType.startsWith("UI");
return (
this.report &&
this.report.reportType &&
this.report.reportType.startsWith('UI')
);
},
},
methods: {
@ -64,7 +97,11 @@ export default {
if (!value) return true;
if (data.value) {
if (value === 'FAKE_ERROR') {
if (data.errorCode && data.errorCode !== "" && data.value.status === "FAKE_ERROR") {
if (
data.errorCode &&
data.errorCode !== '' &&
data.value.status === 'FAKE_ERROR'
) {
return true;
}
} else if (value === 'PENDING') {
@ -83,20 +120,20 @@ export default {
});
},
requestResult(requestResult) {
this.$emit("requestResult", requestResult);
this.$emit('requestResult', requestResult);
},
nodeClick(node) {
node.expanded = !node.expanded;
},
//
changeTreeNodeStatus(node, expandCount) {
node.expanded = this.expandAll
node.expanded = this.expandAll;
for (let i = 0; i < node.childNodes.length; i++) {
// expanded
node.childNodes[i].expanded = this.expandAll
node.childNodes[i].expanded = this.expandAll;
//
if (node.childNodes[i].childNodes.length > 0) {
this.changeTreeNodeStatus(node.childNodes[i])
this.changeTreeNodeStatus(node.childNodes[i]);
}
}
},
@ -109,10 +146,10 @@ export default {
this.isActive = true;
this.expandAll = true;
//
this.changeTreeNodeStatus(this.$refs.resultsTree.store.root, 0)
this.changeTreeNodeStatus(this.$refs.resultsTree.store.root, 0);
},
}
}
},
};
</script>
<style scoped>
@ -143,7 +180,8 @@ export default {
:deep(.el-checkbox) {
color: #303133;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
Arial, sans-serif;
font-size: 13px;
font-weight: normal;
}
@ -163,13 +201,12 @@ export default {
}
.ms-open-btn:hover {
background-color: #F2F9EE;
background-color: #f2f9ee;
cursor: pointer;
color: #67C23A;
color: #67c23a;
}
.ms-open-btn-left {
margin-left: 30px;
}
</style>

View File

@ -6,16 +6,22 @@
:data="table.tableData"
border
size="mini"
highlight-current-row>
<el-table-column v-for="(title, index) in table.titles" :key="index" :label="title" min-width="150px">
highlight-current-row
>
<el-table-column
v-for="(title, index) in table.titles"
:key="index"
:label="title"
min-width="150px"
>
<template v-slot:default="scope">
<el-popover
placement="top"
trigger="click">
<el-popover placement="top" trigger="click">
<el-container>
<div>{{ scope.row[title] }}</div>
</el-container>
<span class="table-content" slot="reference">{{ scope.row[title] }}</span>
<span class="table-content" slot="reference">{{
scope.row[title]
}}</span>
</el-popover>
</template>
</el-table-column>
@ -24,98 +30,99 @@
</template>
<script>
export default {
name: "MsSqlResultTable",
data() {
return {
tables: [],
titles: []
}
},
props: {
body: String
},
created() {
if (!this.body) {
return;
}
let rowArray = this.body.split("\n");
this.getTableData(rowArray);
if (this.tables.length > 1) {
for (let i = 0; i < this.tables.length; i++) {
if (this.tables[i].titles.length === 1 && i < this.tables.length - 1) {
this.tables[i].tableData.splice(this.tables[i].tableData.length - 1, 1);
}
}
let lastTable = this.tables[this.tables.length - 1];
if (lastTable.titles.length === 1) {
if (lastTable.tableData.length > 4) {
lastTable.tableData.splice(lastTable.tableData.length - 4, 4);
} else {
this.tables.splice(this.tables.length - 1, 1);
}
} else {
this.tables.splice(this.tables.length - 1, 1);
}
} else {
let table = this.tables[0];
table.tableData.splice(table.tableData.length - 4, 4);
}
},
methods: {
getTableData(rowArray) {
let titles;
let result = [];
for (let i = 0; i < rowArray.length; i++) {
let colArray = rowArray[i].split("\t");
if (i === 0) {
titles = colArray;
} else {
if (colArray.length != titles.length) {
//
if (colArray.length === 1 && colArray[0] === '') {
this.getTableData(rowArray.slice(i + 1));
} else {
this.getTableData(rowArray.slice(i));
}
break;
} else {
let item = {};
for (let j = 0; j < colArray.length; j++) {
item[titles[j]] = (colArray[j] ? colArray[j] : "");
}
result.push(item);
}
}
}
this.tables.splice(0, 0, {
titles: titles,
tableData: result
});
export default {
name: 'MsSqlResultTable',
data() {
return {
tables: [],
titles: [],
};
},
props: {
body: String,
},
created() {
if (!this.body) {
return;
}
let rowArray = this.body.split('\n');
this.getTableData(rowArray);
if (this.tables.length > 1) {
for (let i = 0; i < this.tables.length; i++) {
if (this.tables[i].titles.length === 1 && i < this.tables.length - 1) {
this.tables[i].tableData.splice(
this.tables[i].tableData.length - 1,
1
);
}
}
let lastTable = this.tables[this.tables.length - 1];
if (lastTable.titles.length === 1) {
if (lastTable.tableData.length > 4) {
lastTable.tableData.splice(lastTable.tableData.length - 4, 4);
} else {
this.tables.splice(this.tables.length - 1, 1);
}
} else {
this.tables.splice(this.tables.length - 1, 1);
}
} else {
let table = this.tables[0];
table.tableData.splice(table.tableData.length - 4, 4);
}
},
methods: {
getTableData(rowArray) {
let titles;
let result = [];
for (let i = 0; i < rowArray.length; i++) {
let colArray = rowArray[i].split('\t');
if (i === 0) {
titles = colArray;
} else {
if (colArray.length != titles.length) {
//
if (colArray.length === 1 && colArray[0] === '') {
this.getTableData(rowArray.slice(i + 1));
} else {
this.getTableData(rowArray.slice(i));
}
break;
} else {
let item = {};
for (let j = 0; j < colArray.length; j++) {
item[titles[j]] = colArray[j] ? colArray[j] : '';
}
result.push(item);
}
}
}
this.tables.splice(0, 0, {
titles: titles,
tableData: result,
});
},
},
};
</script>
<style scoped>
.el-table {
margin-bottom: 20px;
}
.el-table {
margin-bottom: 20px;
}
.el-table :deep(.cell) {
white-space: nowrap;
}
.el-table :deep(.cell) {
white-space: nowrap;
}
.table-content {
cursor: pointer;
}
.el-container {
overflow:auto;
max-height: 500px;
}
.table-content {
cursor: pointer;
}
.el-container {
overflow: auto;
max-height: 500px;
}
</style>

View File

@ -1,50 +1,98 @@
<template>
<el-dialog :close-on-click-modal="false" :title="$t('api_test.automation.add_scenario')" :visible.sync="visible"
width="45%"
:destroy-on-close="true">
<el-form :model="scenarioForm" label-position="right" label-width="100px" size="small" :rules="rule"
ref="scenarioForm">
<el-dialog
:close-on-click-modal="false"
:title="$t('api_test.automation.add_scenario')"
:visible.sync="visible"
width="45%"
:destroy-on-close="true"
>
<el-form
:model="scenarioForm"
label-position="right"
label-width="100px"
size="small"
:rules="rule"
ref="scenarioForm"
>
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="scenarioForm.name" autocomplete="off" :placeholder="$t('commons.name')"/>
<el-input
v-model="scenarioForm.name"
autocomplete="off"
:placeholder="$t('commons.name')"
/>
</el-form-item>
<el-form-item :label="$t('test_track.module.module')" prop="apiScenarioModuleId">
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="scenarioForm.apiScenarioModuleId"
@getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
<el-form-item
:label="$t('test_track.module.module')"
prop="apiScenarioModuleId"
>
<ms-select-tree
size="small"
:data="moduleOptions"
:defaultKey="scenarioForm.apiScenarioModuleId"
@getValue="setModule"
:obj="moduleObj"
clearable
checkStrictly
/>
</el-form-item>
<el-form-item :label="$t('api_test.automation.scenario.principal')" prop="principal">
<el-select v-model="scenarioForm.principal"
:placeholder="$t('api_test.automation.scenario.principal')" filterable size="small"
style="width: 100%">
<el-form-item
:label="$t('api_test.automation.scenario.principal')"
prop="principal"
>
<el-select
v-model="scenarioForm.principal"
:placeholder="$t('api_test.automation.scenario.principal')"
filterable
size="small"
style="width: 100%"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.id + ' (' + item.name + ')'"
:value="item.id">
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('api_test.automation.scenario.follow_people')" prop="followPeople">
<el-select v-model="scenarioForm.follows"
multiple
:placeholder="$t('api_test.automation.scenario.follow_people')" filterable size="small"
style="width: 100%">
<el-form-item
:label="$t('api_test.automation.scenario.follow_people')"
prop="followPeople"
>
<el-select
v-model="scenarioForm.follows"
multiple
:placeholder="$t('api_test.automation.scenario.follow_people')"
filterable
size="small"
style="width: 100%"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.id + ' (' + item.name + ')'"
:value="item.id">
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('commons.description')" prop="description" style="margin-bottom: 29px">
<el-input class="ms-http-textarea" v-model="scenarioForm.description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 10}"
:rows="2" size="small"/>
<el-form-item
:label="$t('commons.description')"
prop="description"
style="margin-bottom: 29px"
>
<el-input
class="ms-http-textarea"
v-model="scenarioForm.description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 10 }"
:rows="2"
size="small"
/>
</el-form-item>
</el-form>
@ -54,31 +102,33 @@
:isShow="true"
:title="$t('commons.edit_info')"
@saveAsEdit="saveScenario(true)"
@confirm="saveScenario">
@confirm="saveScenario"
>
</ms-dialog-footer>
</template>
</el-dialog>
</template>
<script>
import {getMaintainer} from "@/api/project";
import {getCurrentProjectID, getCurrentUser} from "metersphere-frontend/src/utils/token";
import {getUUID} from "metersphere-frontend/src/utils";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import {saveScenario} from "@/business/automation/api-automation";
import MsSelectTree from "metersphere-frontend/src/components/select-tree/SelectTree";
import { getMaintainer } from '@/api/project';
import {
getCurrentProjectID,
getCurrentUser,
} from 'metersphere-frontend/src/utils/token';
import { getUUID } from 'metersphere-frontend/src/utils';
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
import { saveScenario } from '@/business/automation/api-automation';
import MsSelectTree from 'metersphere-frontend/src/components/select-tree/SelectTree';
export default {
name: "MsAddBasisScenario",
components: {MsDialogFooter, MsSelectTree},
name: 'MsAddBasisScenario',
components: { MsDialogFooter, MsSelectTree },
props: {
moduleOptions: Array,
},
data() {
return {
scenarioForm: {follows: []},
scenarioForm: { follows: [] },
visible: false,
currentModule: {},
userOptions: [],
@ -88,17 +138,33 @@ export default {
},
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 100, message: this.$t('test_track.length_less_than') + '100', trigger: 'blur'}
{
required: true,
message: this.$t('test_track.case.input_name'),
trigger: 'blur',
},
{
max: 100,
message: this.$t('test_track.length_less_than') + '100',
trigger: 'blur',
},
],
apiScenarioModuleId: [
{
required: true,
message: this.$t('test_track.case.input_module'),
trigger: 'change',
},
],
principal: [
{
required: true,
message: this.$t('api_test.automation.scenario.select_principal'),
trigger: 'change',
},
],
apiScenarioModuleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
principal: [{
required: true,
message: this.$t('api_test.automation.scenario.select_principal'),
trigger: 'change'
}],
},
}
};
},
computed: {
projectId() {
@ -113,12 +179,14 @@ export default {
saveScenario(saveAs) {
this.$refs['scenarioForm'].validate((valid) => {
if (valid) {
let path = "/api/automation/create";
let path = '/api/automation/create';
this.setParameter();
this.scenarioForm.status = 'Underway';
this.scenarioForm.level = 'P0';
if (saveAs) {
this.scenarioForm.request = JSON.stringify(this.scenarioForm.request);
this.scenarioForm.request = JSON.stringify(
this.scenarioForm.request
);
this.$emit('saveAsEdit', this.scenarioForm);
this.visible = false;
} else {
@ -131,7 +199,7 @@ export default {
} else {
return false;
}
})
});
},
setParameter() {
this.scenarioForm.projectId = this.projectId;
@ -139,28 +207,31 @@ export default {
this.scenarioForm.protocol = this.currentProtocol;
if (!this.scenarioForm.modulePath) {
this.scenarioForm.modulePath = this.$t("commons.module_title");
this.scenarioForm.modulePath = this.$t('commons.module_title');
}
if (!this.scenarioForm.apiScenarioModuleId) {
this.scenarioForm.apiScenarioModuleId = "default-module";
this.scenarioForm.apiScenarioModuleId = 'default-module';
}
},
getMaintainerOptions() {
getMaintainer().then(response => {
getMaintainer().then((response) => {
this.userOptions = response.data;
});
},
open(currentModule) {
this.scenarioForm = {principal: getCurrentUser().id};
this.scenarioForm = { principal: getCurrentUser().id };
this.currentModule = currentModule;
if (this.scenarioForm.apiScenarioModuleId === undefined && !currentModule.id) {
if (
this.scenarioForm.apiScenarioModuleId === undefined &&
!currentModule.id
) {
this.scenarioForm.apiScenarioModuleId = this.moduleOptions[0].id;
} else if (currentModule.id) {
this.scenarioForm.apiScenarioModuleId = currentModule.id;
}
this.getMaintainerOptions();
this.visible = true;
}
}
}
},
},
};
</script>

View File

@ -3,9 +3,17 @@
<el-card>
<el-form :model="request" label-width="auto" ref="request">
<el-form-item :label="$t('api_test.request.name')" prop="name">
<el-input v-model="request.name" maxlength="200" show-word-limit size="small"/>
<el-input
v-model="request.name"
maxlength="200"
show-word-limit
size="small"
/>
</el-form-item>
<el-form-item :label="$t('api_test.definition.api_type')" prop="protocol">
<el-form-item
:label="$t('api_test.definition.api_type')"
prop="protocol"
>
<el-radio v-model="request.protocol" label="HTTP">HTTP</el-radio>
<el-radio v-model="request.protocol" label="DUBBO">DUBBO</el-radio>
<el-radio v-model="request.protocol" label="SQL">SQL</el-radio>
@ -13,37 +21,59 @@
</el-form-item>
</el-form>
<!--不同协议请求-->
<ms-debug-http-page :scenario="true" :current-api="request" @saveAs="editApi" :currentProtocol="request.protocol"
v-if="request.protocol==='HTTP'"/>
<ms-debug-jdbc-page :scenario="true" :currentProtocol="request.protocol" @saveAs="editApi"
v-if="request.protocol==='SQL'"/>
<ms-debug-tcp-page :scenario="true" :currentProtocol="request.protocol" @saveAs="editApi"
v-if="request.protocol==='TCP'"/>
<ms-debug-dubbo-page :scenario="true" :currentProtocol="request.protocol" @saveAs="editApi"
v-if="request.protocol==='DUBBO'"/>
<ms-debug-http-page
:scenario="true"
:current-api="request"
@saveAs="editApi"
:currentProtocol="request.protocol"
v-if="request.protocol === 'HTTP'"
/>
<ms-debug-jdbc-page
:scenario="true"
:currentProtocol="request.protocol"
@saveAs="editApi"
v-if="request.protocol === 'SQL'"
/>
<ms-debug-tcp-page
:scenario="true"
:currentProtocol="request.protocol"
@saveAs="editApi"
v-if="request.protocol === 'TCP'"
/>
<ms-debug-dubbo-page
:scenario="true"
:currentProtocol="request.protocol"
@saveAs="editApi"
v-if="request.protocol === 'DUBBO'"
/>
</el-card>
</div>
</template>
<script>
import MsDebugHttpPage from "../../definition/components/debug/DebugHttpPage";
import MsDebugJdbcPage from "../../definition/components/debug/DebugJdbcPage";
import MsDebugTcpPage from "../../definition/components/debug/DebugTcpPage";
import MsDebugDubboPage from "../../definition/components/debug/DebugDubboPage";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getUUID} from "metersphere-frontend/src/utils";
import MsDebugHttpPage from '../../definition/components/debug/DebugHttpPage';
import MsDebugJdbcPage from '../../definition/components/debug/DebugJdbcPage';
import MsDebugTcpPage from '../../definition/components/debug/DebugTcpPage';
import MsDebugDubboPage from '../../definition/components/debug/DebugDubboPage';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { getUUID } from 'metersphere-frontend/src/utils';
export default {
name: "ApiCustomize",
name: 'ApiCustomize',
props: {
node: {},
request: {},
},
components: {MsDebugHttpPage, MsDebugJdbcPage, MsDebugTcpPage, MsDebugDubboPage},
components: {
MsDebugHttpPage,
MsDebugJdbcPage,
MsDebugTcpPage,
MsDebugDubboPage,
},
data() {
return {
loading: false,
}
};
},
methods: {
remove() {
@ -75,15 +105,13 @@ export default {
this.$emit('addCustomizeApi', obj);
},
reload() {
this.loading = true
this.loading = true;
this.$nextTick(() => {
this.loading = false
})
this.loading = false;
});
},
}
}
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -4,22 +4,39 @@
:visible.sync="dialogVisible"
width="30%"
:destroy-on-close="true"
:before-close="handleClose">
:before-close="handleClose"
>
<div v-loading="result">
<div v-for="pe in data" :key="pe.id" style="margin-left: 20px;">
<div v-for="pe in data" :key="pe.id" style="margin-left: 20px">
{{ getProjectName(pe.id) }}
<el-select v-model="pe['selectEnv']" placeholder="请选择环境" style="margin-left:10px; margin-top: 10px;"
size="small">
<el-option v-for="(environment, index) in pe.envs" :key="index"
:label="environment.name"
:value="environment.id"/>
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)">
<el-select
v-model="pe['selectEnv']"
placeholder="请选择环境"
style="margin-left: 10px; margin-top: 10px"
size="small"
>
<el-option
v-for="(environment, index) in pe.envs"
:key="index"
:label="environment.name"
:value="environment.id"
/>
<el-button
class="ms-scenario-button"
size="mini"
type="primary"
@click="openEnvironmentConfig(pe.id)"
>
{{ $t('api_test.environment.environment_config') }}
</el-button>
<template v-slot:empty>
<div class="empty-environment">
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)">
<el-button
class="ms-scenario-button"
size="mini"
type="primary"
@click="openEnvironmentConfig(pe.id)"
>
{{ $t('api_test.environment.environment_config') }}
</el-button>
</div>
@ -30,27 +47,31 @@
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleConfirm" size="small"> </el-button>
<el-button type="primary" @click="handleConfirm" size="small"
> </el-button
>
</span>
<!-- 环境配置 -->
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/>
<api-environment-config
ref="environmentConfig"
@close="environmentConfigClose"
/>
</el-dialog>
</template>
<script>
import {parseEnvironment} from "@/business/environment/model/EnvironmentModel";
import ApiEnvironmentConfig from "metersphere-frontend/src/components/environment/ApiEnvironmentConfig";
import {getEnvironmentByProjectId} from "metersphere-frontend/src/api/environment";
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
import ApiEnvironmentConfig from 'metersphere-frontend/src/components/environment/ApiEnvironmentConfig';
import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
export default {
name: "ApiScenarioEnv",
components: {ApiEnvironmentConfig},
name: 'ApiScenarioEnv',
components: { ApiEnvironmentConfig },
props: {
envMap: Map,
projectIds: Set,
projectList: Array
projectList: Array,
},
data() {
return {
@ -59,28 +80,28 @@ export default {
projects: [],
environmentId: '',
environments: [],
dialogVisible: false
}
dialogVisible: false,
};
},
methods: {
handleClose() {
this.dialogVisible = false;
},
init() {
this.projectIds.forEach(id => {
let item = {id: id, envs: [], selectEnv: ""};
this.projectIds.forEach((id) => {
let item = { id: id, envs: [], selectEnv: '' };
this.data.push(item);
this.result = getEnvironmentByProjectId(id).then(res => {
this.result = getEnvironmentByProjectId(id).then((res) => {
let envs = res.data;
envs.forEach(environment => {
envs.forEach((environment) => {
parseEnvironment(environment);
});
//
let temp = this.data.find(dt => dt.id === id);
let temp = this.data.find((dt) => dt.id === id);
temp.envs = envs;
temp.selectEnv = this.envMap.get(id);
})
})
});
});
},
open() {
this.data = [];
@ -90,8 +111,8 @@ export default {
}
},
getProjectName(id) {
const project = this.projectList.find(p => p.id === id);
return project ? project.name : "";
const project = this.projectList.find((p) => p.id === id);
return project ? project.name : '';
},
openEnvironmentConfig(projectId) {
if (!projectId) {
@ -103,15 +124,15 @@ export default {
handleConfirm() {
let map = new Map();
let sign = true;
this.data.forEach(dt => {
this.data.forEach((dt) => {
if (!dt.selectEnv) {
sign = false;
return;
}
map.set(dt.id, dt.selectEnv);
})
});
if (!sign) {
this.$warning("请为当前场景选择一个运行环境!");
this.$warning('请为当前场景选择一个运行环境!');
return;
}
this.$emit('setProjectEnvMap', map);
@ -120,18 +141,18 @@ export default {
checkEnv() {
let sign = true;
if (this.data.length > 0) {
this.data.forEach(dt => {
this.data.forEach((dt) => {
if (!dt.selectEnv) {
sign = false;
return false;
}
})
});
} else {
sign = false;
}
if (!sign) {
this.$warning("请为当前场景选择一个运行环境!");
this.$warning('请为当前场景选择一个运行环境!');
return false;
}
return true;
@ -139,9 +160,9 @@ export default {
environmentConfigClose() {
this.data = [];
this.init();
}
}
}
},
},
};
</script>
<style scoped>

View File

@ -1,6 +1,5 @@
<template>
<div>
<slot name="header"></slot>
<ms-node-tree
@ -22,39 +21,47 @@
@refresh="list"
@filter="filter"
@nodeSelectEvent="nodeChange"
ref="nodeTree">
ref="nodeTree"
>
<template v-slot:header>
<ms-search-bar
:show-operator="showOperator && !isTrashData"
:condition="condition"
:commands="operators"/>
<module-trash-button v-if="!isReadOnly && !isTrashData" :condition="condition" :exe="enableTrash"
:total='total'/>
:commands="operators"
/>
<module-trash-button
v-if="!isReadOnly && !isTrashData"
:condition="condition"
:exe="enableTrash"
:total="total"
/>
</template>
</ms-node-tree>
<ms-add-basis-scenario
:module-options="data"
@saveAsEdit="saveAsEdit"
@refresh="refresh"
ref="basisScenario"/>
ref="basisScenario"
/>
<api-import ref="apiImport" :moduleOptions="data" @refreshAll="$emit('refreshAll')"/>
<api-import
ref="apiImport"
:moduleOptions="data"
@refreshAll="$emit('refreshAll')"
/>
</div>
</template>
<script>
import SelectMenu from "@/business/commons/SelectMenu";
import MsAddBasisScenario from "@/business/automation/scenario/AddBasisScenario";
import MsNodeTree from "@/business/commons/NodeTree";
import {buildTree} from "metersphere-frontend/src/model/NodeTree";
import ModuleTrashButton from "../../definition/components/module/ModuleTrashButton";
import ApiImport from "./common/ScenarioImport";
import MsSearchBar from "metersphere-frontend/src/components/search/MsSearchBar";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import SelectMenu from '@/business/commons/SelectMenu';
import MsAddBasisScenario from '@/business/automation/scenario/AddBasisScenario';
import MsNodeTree from '@/business/commons/NodeTree';
import { buildTree } from 'metersphere-frontend/src/model/NodeTree';
import ModuleTrashButton from '../../definition/components/module/ModuleTrashButton';
import ApiImport from './common/ScenarioImport';
import MsSearchBar from 'metersphere-frontend/src/components/search/MsSearchBar';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import {
addScenarioModule,
delScenarioModule,
@ -63,8 +70,8 @@ import {
getModuleByProjectId,
getModuleByRelevanceProjectId,
getModuleByTrash,
posScenarioModule
} from "@/api/scenario-module";
posScenarioModule,
} from '@/api/scenario-module';
export default {
name: 'MsApiScenarioModule',
@ -81,7 +88,7 @@ export default {
type: Boolean,
default() {
return false;
}
},
},
showOperator: Boolean,
relevanceProjectId: String,
@ -92,14 +99,14 @@ export default {
type: Boolean,
default() {
return true;
}
},
},
selectProjectId: {
type: String,
default() {
return getCurrentProjectID();
}
}
},
},
},
computed: {
isRelevanceModel() {
@ -111,14 +118,14 @@ export default {
} else {
return getCurrentProjectID();
}
}
},
},
data() {
return {
result: false,
condition: {
filterText: "",
trashEnable: false
filterText: '',
trashEnable: false,
},
data: [],
currentModule: undefined,
@ -126,12 +133,12 @@ export default {
{
label: this.$t('api_test.automation.add_scenario'),
callback: this.addScenario,
permissions: ['PROJECT_API_SCENARIO:READ+CREATE']
permissions: ['PROJECT_API_SCENARIO:READ+CREATE'],
},
{
label: this.$t('api_test.api_import.label'),
callback: this.handleImport,
permissions: ['PROJECT_API_SCENARIO:READ+IMPORT_SCENARIO']
permissions: ['PROJECT_API_SCENARIO:READ+IMPORT_SCENARIO'],
},
{
label: this.$t('report.export'),
@ -141,19 +148,19 @@ export default {
permissions: ['PROJECT_API_SCENARIO:READ+EXPORT_SCENARIO'],
callback: () => {
this.exportAPI();
}
},
},
{
label: this.$t('report.export_jmeter_format'),
permissions: ['PROJECT_API_SCENARIO:READ+EXPORT_SCENARIO'],
callback: () => {
this.$emit('exportJmx');
}
}
]
}
]
}
},
},
],
},
],
};
},
mounted() {
this.list();
@ -171,16 +178,16 @@ export default {
isTrashData() {
this.condition.trashEnable = this.isTrashData;
this.list();
}
},
},
methods: {
handleImport() {
if (this.projectId) {
this.result = getModuleByProjectId(this.projectId).then(response => {
this.result = getModuleByProjectId(this.projectId).then((response) => {
if (response.data != undefined && response.data != null) {
this.data = response.data;
this.data.forEach(node => {
buildTree(node, {path: ''});
this.data.forEach((node) => {
buildTree(node, { path: '' });
});
}
});
@ -192,15 +199,21 @@ export default {
},
list(projectId) {
if (this.isRelevanceModel) {
this.result = getModuleByRelevanceProjectId(this.relevanceProjectId).then(response => {
this.result = getModuleByRelevanceProjectId(
this.relevanceProjectId
).then((response) => {
this.setData(response);
});
} else if (this.isTrashData) {
this.result = getModuleByTrash(projectId ? projectId : this.projectId).then(response => {
this.result = getModuleByTrash(
projectId ? projectId : this.projectId
).then((response) => {
this.setData(response);
});
} else {
this.result = getModuleByProjectId(projectId ? projectId : this.projectId).then(response => {
this.result = getModuleByProjectId(
projectId ? projectId : this.projectId
).then((response) => {
this.setData(response);
});
}
@ -208,9 +221,12 @@ export default {
setData(response) {
if (response.data != undefined && response.data != null) {
this.data = response.data;
this.data.forEach(node => {
node.name = node.name === '未规划场景' ? this.$t('api_test.automation.unplanned_scenario') : node.name
buildTree(node, {path: ''});
this.data.forEach((node) => {
node.name =
node.name === '未规划场景'
? this.$t('api_test.automation.unplanned_scenario')
: node.name;
buildTree(node, { path: '' });
});
this.$emit('setModuleOptions', this.data);
this.$emit('setNodeTree', this.data);
@ -222,13 +238,16 @@ export default {
edit(param) {
param.projectId = this.projectId;
param.protocol = this.condition.protocol;
editScenarioModule(param).then(() => {
this.$success(this.$t('commons.save_success'));
this.list();
this.refresh();
}, (error) => {
this.list();
});
editScenarioModule(param).then(
() => {
this.$success(this.$t('commons.save_success'));
this.list();
this.refresh();
},
(error) => {
this.list();
}
);
},
add(param) {
param.projectId = this.projectId;
@ -238,71 +257,83 @@ export default {
this.$error(this.$t('commons.warning_module_add'));
return;
} else {
addScenarioModule(param).then(() => {
this.$success(this.$t('commons.save_success'));
this.list();
}, (error) => {
this.list();
});
addScenarioModule(param).then(
() => {
this.$success(this.$t('commons.save_success'));
this.list();
},
(error) => {
this.list();
}
);
}
},
remove(nodeIds) {
delScenarioModule(nodeIds).then(() => {
this.list();
this.refresh();
this.removeModuleId(nodeIds);
}, (error) => {
this.list();
});
delScenarioModule(nodeIds).then(
() => {
this.list();
this.refresh();
this.removeModuleId(nodeIds);
},
(error) => {
this.list();
}
);
},
drag(param, list) {
dragScenarioModule(param).then(() => {
posScenarioModule(list).then(() => {
dragScenarioModule(param).then(
() => {
posScenarioModule(list).then(() => {
this.list();
});
},
(error) => {
this.list();
});
}, (error) => {
this.list();
});
}
);
},
nodeChange(node, nodeIds, pNodes) {
this.currentModule = node.data;
if (node.data.id === 'root') {
this.$emit("nodeSelectEvent", node, [], pNodes);
this.$emit('nodeSelectEvent', node, [], pNodes);
} else {
this.$emit("nodeSelectEvent", node, nodeIds, pNodes);
this.$emit('nodeSelectEvent', node, nodeIds, pNodes);
}
this.nohupReloadTree(node.data.id);
},
//
nohupReloadTree(selectNodeId) {
if (this.isRelevanceModel) {
getModuleByRelevanceProjectId(this.relevanceProjectId).then(response => {
this.setModuleList(response, selectNodeId);
});
getModuleByRelevanceProjectId(this.relevanceProjectId).then(
(response) => {
this.setModuleList(response, selectNodeId);
}
);
} else if (this.isTrashData) {
if (!this.projectId) {
return;
}
getModuleByTrash(this.projectId).then(response => {
getModuleByTrash(this.projectId).then((response) => {
this.setModuleList(response, selectNodeId);
});
} else {
if (!this.projectId) {
return;
}
getModuleByProjectId(this.projectId).then(response => {
getModuleByProjectId(this.projectId).then((response) => {
this.setModuleList(response, selectNodeId);
});
}
},
setModuleList(response, selectNodeId) {
if (response.data != undefined && response.data != null) {
this.data = response.data;
this.data.forEach(node => {
node.name = node.name === '未规划场景' ? this.$t('api_test.automation.unplanned_scenario') : node.name
buildTree(node, {path: ''});
this.data.forEach((node) => {
node.name =
node.name === '未规划场景'
? this.$t('api_test.automation.unplanned_scenario')
: node.name;
buildTree(node, { path: '' });
});
this.$nextTick(() => {
@ -312,7 +343,7 @@ export default {
this.$refs.nodeTree.justSetCurrentKey(selectNodeId);
}
}
})
});
}
},
exportAPI() {
@ -322,7 +353,7 @@ export default {
this.$emit('saveAsEdit', data);
},
refresh() {
this.$emit("refreshAll");
this.$emit('refreshAll');
},
addScenario() {
if (!this.projectId) {
@ -336,12 +367,15 @@ export default {
this.$emit('enableTrash', this.condition.trashEnable);
},
removeModuleId(nodeIds) {
if (localStorage.getItem('scenarioModule') && localStorage.getItem('scenarioModule') === nodeIds[0]) {
if (
localStorage.getItem('scenarioModule') &&
localStorage.getItem('scenarioModule') === nodeIds[0]
) {
localStorage.setItem('scenarioModule', undefined);
}
}
}
}
},
},
};
</script>
<style scoped>
@ -394,5 +428,4 @@ export default {
.ms-api-buttion {
width: 30px;
}
</style>

View File

@ -6,22 +6,47 @@
<div class="kv-row" v-for="(item, index) in items" :key="index">
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
<el-col class="kv-checkbox">
<input type="checkbox" v-if="!isDisable(index)" @change="change" :value="item.uuid" v-model="item.enable"
:disabled="isDisable(index) || isReadOnly"/>
<input
type="checkbox"
v-if="!isDisable(index)"
@change="change"
:value="item.uuid"
v-model="item.enable"
:disabled="isDisable(index) || isReadOnly"
/>
</el-col>
<el-col>
<ms-api-variable-input :show-copy="showCopy" :show-variable="showVariable" :is-read-only="isReadOnly"
v-model="item.name" size="small" maxlength="200" @change="change"
:placeholder="$t('api_test.variable_name')" show-word-limit/>
<ms-api-variable-input
:show-copy="showCopy"
:show-variable="showVariable"
:is-read-only="isReadOnly"
v-model="item.name"
size="small"
maxlength="200"
@change="change"
:placeholder="$t('api_test.variable_name')"
show-word-limit
/>
</el-col>
<el-col>
<el-input :disabled="isReadOnly" v-model="item.value" size="small" @change="change"
:placeholder="$t('api_test.value')" show-word-limit/>
<el-input
:disabled="isReadOnly"
v-model="item.value"
size="small"
@change="change"
:placeholder="$t('api_test.value')"
show-word-limit
/>
</el-col>
<el-col class="kv-delete">
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
:disabled="isDisable(index) || isReadOnly"/>
<el-button
size="mini"
class="el-icon-delete-solid"
circle
@click="remove(index)"
:disabled="isDisable(index) || isReadOnly"
/>
</el-col>
</el-row>
</div>
@ -29,30 +54,30 @@
</template>
<script>
import {KeyValue} from "../../definition/model/ApiTestModel";
import MsApiVariableInput from "./ApiVariableInput";
import { KeyValue } from '../../definition/model/ApiTestModel';
import MsApiVariableInput from './ApiVariableInput';
export default {
name: "MsApiScenarioVariables",
components: {MsApiVariableInput},
name: 'MsApiScenarioVariables',
components: { MsApiVariableInput },
props: {
description: String,
items: Array,
isReadOnly: {
type: Boolean,
default: false
default: false,
},
showVariable: {
type: Boolean,
default: true
default: true,
},
showCopy: {
type: Boolean,
default: true
default: true,
},
},
data() {
return {}
return {};
},
methods: {
remove: function (index) {
@ -77,22 +102,22 @@ export default {
});
}
if (isNeedCreate) {
this.items.push(new KeyValue({enable: true}));
this.items.push(new KeyValue({ enable: true }));
}
this.$emit('change', this.items);
// TODO key
},
isDisable: function (index) {
return this.items.length - 1 === index;
}
},
},
created() {
if (this.items.length === 0) {
this.items.push(new KeyValue({enable: true}));
this.items.push(new KeyValue({ enable: true }));
}
}
}
},
};
</script>
<style scoped>

View File

@ -1,123 +1,142 @@
<template>
<div class="variable-input" :class="{'show-copy': !showCopy}">
<el-input class="el-input__inner_pd" :disabled="isReadOnly" :value="value" v-bind="$attrs" :size="size" @change="change" @input="input"/>
<div :class="{'hidden': !showVariable}" class="variable-combine" v-if="value">
<div v-if="showCopy" class="variable">{{variable}}</div>
<el-tooltip v-if="showCopy" :content="$t('api_test.copied')" manual v-model="visible" placement="top" :visible-arrow="false">
<i class="el-icon-copy-document copy" @click="copy"/>
<div class="variable-input" :class="{ 'show-copy': !showCopy }">
<el-input
class="el-input__inner_pd"
:disabled="isReadOnly"
:value="value"
v-bind="$attrs"
:size="size"
@change="change"
@input="input"
/>
<div
:class="{ hidden: !showVariable }"
class="variable-combine"
v-if="value"
>
<div v-if="showCopy" class="variable">{{ variable }}</div>
<el-tooltip
v-if="showCopy"
:content="$t('api_test.copied')"
manual
v-model="visible"
placement="top"
:visible-arrow="false"
>
<i class="el-icon-copy-document copy" @click="copy" />
</el-tooltip>
</div>
</div>
</template>
<script>
export default {
name: "MsApiVariableInput",
export default {
name: 'MsApiVariableInput',
props: {
value: String,
size: String,
isReadOnly: {
type: Boolean,
default: false
},
showVariable: {
type: Boolean,
default: true
},
showCopy: {
type: Boolean,
default: true
},
showCopyTipWithMultiple: {
type: Boolean,
default: false
},
props: {
value: String,
size: String,
isReadOnly: {
type: Boolean,
default: false,
},
data() {
return {
visible: false
}
showVariable: {
type: Boolean,
default: true,
},
methods: {
copy() {
let input = document.createElement("input");
document.body.appendChild(input);
input.value = this.variable;
input.select();
if (input.setSelectionRange) {
input.setSelectionRange(0, input.value.length);
}
document.execCommand("copy");
document.body.removeChild(input);
this.visible = true;
setTimeout(() => {
this.visible = false;
}, 1000);
},
change(value) {
this.$emit('change', value);
},
input(value) {
this.$emit('input', value);
}
showCopy: {
type: Boolean,
default: true,
},
showCopyTipWithMultiple: {
type: Boolean,
default: false,
},
},
computed: {
variable() {
return "${" + (this.showCopyTipWithMultiple ? (this.value + "_n") : this.value) + "}";
data() {
return {
visible: false,
};
},
methods: {
copy() {
let input = document.createElement('input');
document.body.appendChild(input);
input.value = this.variable;
input.select();
if (input.setSelectionRange) {
input.setSelectionRange(0, input.value.length);
}
}
document.execCommand('copy');
document.body.removeChild(input);
this.visible = true;
setTimeout(() => {
this.visible = false;
}, 1000);
},
change(value) {
this.$emit('change', value);
},
input(value) {
this.$emit('input', value);
},
},
}
computed: {
variable() {
return (
'${' +
(this.showCopyTipWithMultiple ? this.value + '_n' : this.value) +
'}'
);
},
},
};
</script>
<style scoped>
.variable-input {
position: relative;
}
.variable-input {
position: relative;
}
.el-input__inner_pd :deep(.el-input__inner) {
padding-right: 135px;
}
.el-input__inner_pd :deep(.el-input__inner) {
padding-right: 135px;
}
.show-copy .el-input__inner_pd :deep(.el-input__inner) {
padding-right: 0px;
}
.show-copy .el-input__inner_pd :deep(.el-input__inner) {
padding-right: 0px;
}
.variable-combine {
color: #7f7f7f;
max-width: 80px;
line-height: 32px;
position: absolute;
top: 0;
right: 70px;
margin-right: -20px;
display: flex;
align-items: center;
}
.variable-combine .variable {
display: inline-block;
max-width: 60px;
margin-right: 10px;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.variable-combine {
color: #7F7F7F;
max-width: 80px;
line-height: 32px;
position: absolute;
top: 0;
right: 70px;
margin-right: -20px;
display: flex;
align-items: center;
}
.variable-combine .variable {
display: inline-block;
max-width: 60px;
margin-right: 10px;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.variable-combine .copy {
font-size: 14px;
cursor: pointer;
color: #1E90FF;
}
.hidden {
visibility: hidden;
}
.variable-combine .copy {
font-size: 14px;
cursor: pointer;
color: #1e90ff;
}
.hidden {
visibility: hidden;
}
</style>

View File

@ -2,11 +2,11 @@
<div></div>
</template>
<script>
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {strMapToObj} from "metersphere-frontend/src/utils";
import {createComponent} from "../../definition/components/jmeter/components";
import {saveScenario} from "@/business/automation/api-automation";
import {TYPE_TO_C} from "@/business/automation/scenario/Setting";
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { strMapToObj } from 'metersphere-frontend/src/utils';
import { createComponent } from '../../definition/components/jmeter/components';
import { saveScenario } from '@/business/automation/api-automation';
import { TYPE_TO_C } from '@/business/automation/scenario/Setting';
export default {
name: 'MsDebugRun',
@ -23,37 +23,52 @@ export default {
saved: Boolean,
environmentType: String,
environmentGroupId: String,
browserLanguage: String
browserLanguage: String,
},
data() {
return {
result: false,
loading: false,
reqNumber: 0,
}
};
},
watch: {
//
reportId() {
this.run()
}
this.run();
},
},
methods: {
sort(stepArray) {
if (stepArray) {
for (let i in stepArray) {
if (stepArray[i] && TYPE_TO_C.get(stepArray[i].type) && !stepArray[i].clazzName) {
if (
stepArray[i] &&
TYPE_TO_C.get(stepArray[i].type) &&
!stepArray[i].clazzName
) {
stepArray[i].clazzName = TYPE_TO_C.get(stepArray[i].type);
}
if (stepArray[i].type === "Assertions" && !stepArray[i].document) {
if (stepArray[i].type === 'Assertions' && !stepArray[i].document) {
stepArray[i].document = {
type: "JSON",
data: {xmlFollowAPI: false, jsonFollowAPI: false, json: [], xml: []}
type: 'JSON',
data: {
xmlFollowAPI: false,
jsonFollowAPI: false,
json: [],
xml: [],
},
};
}
if (stepArray[i] && stepArray[i].authManager && !stepArray[i].authManager.clazzName) {
stepArray[i].authManager.clazzName = TYPE_TO_C.get(stepArray[i].authManager.type);
if (
stepArray[i] &&
stepArray[i].authManager &&
!stepArray[i].authManager.clazzName
) {
stepArray[i].authManager.clazzName = TYPE_TO_C.get(
stepArray[i].authManager.type
);
}
if (stepArray[i].hashTree && stepArray[i].hashTree.length > 0) {
this.sort(stepArray[i].hashTree);
@ -89,7 +104,7 @@ export default {
environmentMap: strMapToObj(map),
environmentType: this.environmentType,
environmentGroupId: this.environmentGroupId,
environmentJson: JSON.stringify(strMapToObj(map))
environmentJson: JSON.stringify(strMapToObj(map)),
};
if (this.runData.variables) {
reqObj.variables = this.runData.variables;
@ -101,12 +116,14 @@ export default {
let url = '/api/automation/run/debug';
saveScenario(url, reqObj, this.runData.hashTree, this, (response) => {
if (response.data !== "SUCCESS") {
this.$error(response.data ? response.data : this.$t('commons.run_fail'));
if (response.data !== 'SUCCESS') {
this.$error(
response.data ? response.data : this.$t('commons.run_fail')
);
this.$emit('errorRefresh');
}
});
},
}
}
},
};
</script>

View File

@ -1,24 +1,43 @@
<template>
<div>
<div style="margin-left: 20px;">
<el-select v-model="envGroupId" :placeholder="$t('workspace.env_group.select')"
style="margin-top: 8px;width: 200px;" size="small">
<el-option v-for="(group, index) in groups" :key="index"
:label="group.name"
:value="group.id"/>
<div style="margin-left: 20px">
<el-select
v-model="envGroupId"
:placeholder="$t('workspace.env_group.select')"
style="margin-top: 8px; width: 200px"
size="small"
>
<el-option
v-for="(group, index) in groups"
:key="index"
:label="group.name"
:value="group.id"
/>
</el-select>
<span style="margin-left: 8px;">{{ $t('workspace.env_group.name') }}</span>
<span style="margin-left: 8px">{{ $t('workspace.env_group.name') }}</span>
<i class="el-icon-view icon-view-btn" @click="viewGroup"></i>
</div>
<el-button type="primary" @click="handleConfirm" size="small" :style="btnStyle" class="env-confirm">
<el-button
type="primary"
@click="handleConfirm"
size="small"
:style="btnStyle"
class="env-confirm"
>
{{ $t('workspace.env_group.confirm') }}
</el-button>
<el-dialog :visible="visible" append-to-body :title="$t('workspace.env_group.name')" @close="visible = false"
style="height: 800px;">
<el-dialog
:visible="visible"
append-to-body
:title="$t('workspace.env_group.name')"
@close="visible = false"
style="height: 800px"
>
<template>
<environment-group style="overflow-y: auto;"
:screen-height="'350px'"
:read-only="true"
<environment-group
style="overflow-y: auto"
:screen-height="'350px'"
:read-only="true"
></environment-group>
</template>
</el-dialog>
@ -26,38 +45,41 @@
</template>
<script>
import EnvironmentGroup from "@/business/commons/EnvironmentGroupList";
import {environmentGetALL, getEnvironmentMapByGroupId} from "metersphere-frontend/src/api/environment";
import EnvironmentGroup from '@/business/commons/EnvironmentGroupList';
import {
environmentGetALL,
getEnvironmentMapByGroupId,
} from 'metersphere-frontend/src/api/environment';
export default {
name: "EnvGroup",
components: {EnvironmentGroup},
name: 'EnvGroup',
components: { EnvironmentGroup },
data() {
return {
groups: [],
envGroupId: this.groupId,
visible: false
}
visible: false,
};
},
props: {
groupId: {
type: String,
default() {
return "";
}
return '';
},
},
projectIds: Set,
btnStyle: {
type: Object,
default() {
return {width: "360px"}
}
}
return { width: '360px' };
},
},
},
watch: {
groupId(val) {
this.envGroupId = val;
}
},
},
created() {
this.init();
@ -67,10 +89,10 @@ export default {
this.envGroupId = this.groupId;
},
init() {
environmentGetALL().then(res => {
environmentGetALL().then((res) => {
let data = res.data;
this.groups = data ? data : [];
})
});
},
viewGroup() {
this.visible = true;
@ -78,7 +100,7 @@ export default {
async handleConfirm() {
const sign = await this.checkEnv();
if (sign) {
this.$emit("setEnvGroup", this.envGroupId);
this.$emit('setEnvGroup', this.envGroupId);
this.$emit('close');
}
},
@ -89,7 +111,7 @@ export default {
resolve(false);
return false;
}
getEnvironmentMapByGroupId(this.envGroupId).then(res => {
getEnvironmentMapByGroupId(this.envGroupId).then((res) => {
let data = res.data;
if (!data) {
this.$warning(this.$t('workspace.env_group.lack_env'));
@ -99,17 +121,19 @@ export default {
let map = new Map(Object.entries(data));
for (let id of this.projectIds) {
if (!map.get(id)) {
this.$warning(this.$t('workspace.env_group.lack_necessary_environment'));
this.$warning(
this.$t('workspace.env_group.lack_necessary_environment')
);
resolve(false);
return;
}
}
resolve(true);
});
})
}
}
}
});
},
},
};
</script>
<style scoped>

View File

@ -1,33 +1,51 @@
<template>
<div>
<div style="margin-left: 20px;">
<el-select v-model="envGroupId" :placeholder="$t('workspace.env_group.select')"
style="margin-top: 8px;width: 200px;" size="small" clearable>
<div style="margin-left: 20px">
<el-select
v-model="envGroupId"
:placeholder="$t('workspace.env_group.select')"
style="margin-top: 8px; width: 200px"
size="small"
clearable
>
<el-option-group
v-for="group in groups"
:key="group.label"
:label="group.label">
:label="group.label"
>
<el-option
v-for="item in group.options"
:key="item.name"
:label="item.name"
:disabled="item.disabled"
:value="item.id">
:value="item.id"
>
</el-option>
</el-option-group>
</el-select>
<span style="margin-left: 8px;">{{ $t('workspace.env_group.name') }}</span>
<span style="margin-left: 8px">{{ $t('workspace.env_group.name') }}</span>
<i class="el-icon-view icon-view-btn" @click="viewGroup"></i>
</div>
<el-button type="primary" @click="handleConfirm" size="small" class="env-confirm">
<el-button
type="primary"
@click="handleConfirm"
size="small"
class="env-confirm"
>
{{ $t('workspace.env_group.confirm') }}
</el-button>
<el-dialog :visible="visible" append-to-body :title="$t('workspace.env_group.name')" @close="visible = false"
style="height: 800px;">
<el-dialog
:visible="visible"
append-to-body
:title="$t('workspace.env_group.name')"
@close="visible = false"
style="height: 800px"
>
<template>
<environment-group style="overflow-y: auto;"
:screen-height="'350px'"
:read-only="true"
<environment-group
style="overflow-y: auto"
:screen-height="'350px'"
:read-only="true"
></environment-group>
</template>
</el-dialog>
@ -35,12 +53,15 @@
</template>
<script>
import EnvironmentGroup from "@/business/commons/EnvironmentGroupList";
import {getEnvironmentMapByGroupId, getEnvironmentOptions} from "metersphere-frontend/src/api/environment";
import EnvironmentGroup from '@/business/commons/EnvironmentGroupList';
import {
getEnvironmentMapByGroupId,
getEnvironmentOptions,
} from 'metersphere-frontend/src/api/environment';
export default {
name: "EnvGroupWithOption",
components: {EnvironmentGroup},
name: 'EnvGroupWithOption',
components: { EnvironmentGroup },
data() {
return {
groups: [],
@ -48,22 +69,22 @@ export default {
visible: false,
disabledGroups: [],
notDisabledGroups: [],
result: false
}
result: false,
};
},
props: {
groupId: {
type: String,
default() {
return "";
}
return '';
},
},
projectIds: Set,
},
watch: {
groupId(val) {
this.envGroupId = val;
}
},
},
created() {
this.init();
@ -73,19 +94,23 @@ export default {
this.envGroupId = this.groupId;
},
init() {
this.result = getEnvironmentOptions({projectIds: [...this.projectIds]}).then(res => {
this.result = getEnvironmentOptions({
projectIds: [...this.projectIds],
}).then((res) => {
let groups = res.data;
this.disabledGroups = groups.filter(group => group.disabled === true);
this.notDisabledGroups = groups.filter(group => group.disabled === false);
this.disabledGroups = groups.filter((group) => group.disabled === true);
this.notDisabledGroups = groups.filter(
(group) => group.disabled === false
);
this.$set(this.groups, 0, {
label: this.$t('workspace.env_group.available_group'),
options: this.notDisabledGroups
options: this.notDisabledGroups,
});
this.$set(this.groups, 1, {
label: this.$t('workspace.env_group.not_available_group'),
options: this.disabledGroups
options: this.disabledGroups,
});
})
});
},
viewGroup() {
this.visible = true;
@ -93,7 +118,7 @@ export default {
async handleConfirm() {
const sign = await this.checkEnv();
if (sign) {
this.$emit("setEnvGroup", this.envGroupId);
this.$emit('setEnvGroup', this.envGroupId);
this.$emit('close');
}
},
@ -104,7 +129,7 @@ export default {
resolve(false);
return false;
}
getEnvironmentMapByGroupId(this.envGroupId).then(res => {
getEnvironmentMapByGroupId(this.envGroupId).then((res) => {
let data = res.data;
if (!data) {
this.$warning(this.$t('workspace.env_group.lack_env'));
@ -114,17 +139,19 @@ export default {
let map = new Map(Object.entries(data));
for (let id of this.projectIds) {
if (!map.get(id)) {
this.$warning(this.$t('workspace.env_group.lack_necessary_environment'));
this.$warning(
this.$t('workspace.env_group.lack_necessary_environment')
);
resolve(false);
return;
}
}
resolve(true);
});
})
}
}
}
});
},
},
};
</script>
<style scoped>

View File

@ -5,30 +5,58 @@
:width="width"
:disabled="isReadOnly"
@show="showPopover"
trigger="click">
<el-radio-group v-model="radio" style="margin-left: 20px;" @change="radioChange">
<el-radio :label="ENV_TYPE.JSON">{{ $t('workspace.env_group.env_list') }}</el-radio>
<el-radio v-if="showEnvGroup" :label="ENV_TYPE.GROUP">{{ $t('workspace.env_group.name') }}</el-radio>
trigger="click"
>
<el-radio-group
v-model="radio"
style="margin-left: 20px"
@change="radioChange"
>
<el-radio :label="ENV_TYPE.JSON">{{
$t('workspace.env_group.env_list')
}}</el-radio>
<el-radio v-if="showEnvGroup" :label="ENV_TYPE.GROUP">{{
$t('workspace.env_group.name')
}}</el-radio>
</el-radio-group>
<env-select :project-ids="projectIds"
:result="result"
:show-config-button-with-out-permission="showConfigButtonWithOutPermission"
:env-map="envMap"
:project-list="projectList"
@close="visible = false"
@setProjectEnvMap="setProjectEnvMap"
v-show="!radio || radio === ENV_TYPE.JSON"
:btnStyle="btnStyle"
ref="envSelect"/>
<env-group ref="envGroup" v-show="radio === ENV_TYPE.GROUP && !hasOptionGroup" @close="visible = false"
:project-ids="projectIds"
@setEnvGroup="setEnvGroup" :group-id="groupId" :btnStyle="btnStyle"></env-group>
<env-select
:project-ids="projectIds"
:result="result"
:show-config-button-with-out-permission="
showConfigButtonWithOutPermission
"
:env-map="envMap"
:project-list="projectList"
@close="visible = false"
@setProjectEnvMap="setProjectEnvMap"
v-show="!radio || radio === ENV_TYPE.JSON"
:btnStyle="btnStyle"
ref="envSelect"
/>
<env-group
ref="envGroup"
v-show="radio === ENV_TYPE.GROUP && !hasOptionGroup"
@close="visible = false"
:project-ids="projectIds"
@setEnvGroup="setEnvGroup"
:group-id="groupId"
:btnStyle="btnStyle"
></env-group>
<!-- 对环境组选项进行分类 可用不可用 -->
<env-group-with-option ref="envOptionGroup" v-show="radio === ENV_TYPE.GROUP && hasOptionGroup"
@close="visible = false"
:project-ids="projectIds"
@setEnvGroup="setEnvGroup" :group-id="groupId"></env-group-with-option>
<el-button type="primary" slot="reference" size="mini" style="margin-top: 2px;">
<env-group-with-option
ref="envOptionGroup"
v-show="radio === ENV_TYPE.GROUP && hasOptionGroup"
@close="visible = false"
:project-ids="projectIds"
@setEnvGroup="setEnvGroup"
:group-id="groupId"
></env-group-with-option>
<el-button
type="primary"
slot="reference"
size="mini"
style="margin-top: 2px"
>
{{ $t('api_test.definition.request.run_env') }}
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
@ -36,18 +64,18 @@
</template>
<script>
import EnvSelect from "@/business/automation/scenario/EnvSelect";
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
import EnvGroup from "@/business/automation/scenario/EnvGroup";
import EnvGroupWithOption from "@/business/automation/scenario/EnvGroupWithOption";
import EnvSelect from '@/business/automation/scenario/EnvSelect';
import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
import EnvGroup from '@/business/automation/scenario/EnvGroup';
import EnvGroupWithOption from '@/business/automation/scenario/EnvGroupWithOption';
export default {
name: "EnvPopover",
components: {EnvGroup, EnvSelect, EnvGroupWithOption},
name: 'EnvPopover',
components: { EnvGroup, EnvSelect, EnvGroupWithOption },
props: {
width: {
type: String,
default: "400"
default: '400',
},
envMap: Map,
projectIds: Set,
@ -56,78 +84,78 @@ export default {
type: Boolean,
default() {
return true;
}
},
},
isReadOnly: {
type: Boolean,
default() {
return false;
}
},
},
result: {
type: Object,
default() {
return {loading: false}
}
return { loading: false };
},
},
groupId: {
type: String,
default() {
return "";
}
return '';
},
},
environmentType: String,
isScenario: {
type: Boolean,
default() {
return true;
}
},
},
showEnvGroup: {
type: Boolean,
default() {
return true;
}
},
},
placement: {
type: String,
default() {
return "bottom";
}
return 'bottom';
},
},
hasOptionGroup: {
type: Boolean,
default() {
return false;
}
},
},
btnStyle: {
type: Object,
default() {
return {width: "360px"}
}
}
return { width: '360px' };
},
},
},
data() {
return {
visible: false,
radio: this.environmentType,
}
};
},
watch: {
environmentType(val) {
this.radio = val;
}
},
},
computed: {
ENV_TYPE() {
return ENV_TYPE;
}
},
},
methods: {
showPopover() {
if (this.isScenario) {
this.$emit("showPopover");
this.$emit('showPopover');
} else {
this.$refs.envSelect.open();
}
@ -143,16 +171,16 @@ export default {
}
},
setProjectEnvMap(map) {
this.$emit("setProjectEnvMap", map);
this.$emit('setProjectEnvMap', map);
},
setEnvGroup(envGroupId) {
this.$emit("setEnvGroup", envGroupId);
this.$emit('setEnvGroup', envGroupId);
},
initEnv() {
return this.$refs.envSelect.initEnv();
},
checkEnv(data) {
return new Promise((resolve => {
return new Promise((resolve) => {
if (data) {
//
resolve(true);
@ -164,23 +192,21 @@ export default {
let res = this.$refs.envSelect.checkEnv(data);
resolve(res);
} else if (this.environmentType === ENV_TYPE.GROUP) {
let res = !this.hasOptionGroup ? this.$refs.envGroup.checkEnv() :
this.$refs.envOptionGroup.checkEnv();
res.then(r => {
let res = !this.hasOptionGroup
? this.$refs.envGroup.checkEnv()
: this.$refs.envOptionGroup.checkEnv();
res.then((r) => {
resolve(r);
})
});
}
}
}))
});
},
radioChange(val) {
this.$emit("update:environmentType", val);
}
}
}
this.$emit('update:environmentType', val);
},
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -1,19 +1,35 @@
<template>
<div v-loading="result.loading">
<div v-for="pe in data" :key="pe.id" style="margin-left: 20px;">
<el-select v-model="pe['selectEnv']" :placeholder="$t('workspace.env_group.please_select_env')"
style="margin-top: 8px;width: 200px;" size="small">
<el-option v-for="(environment, index) in pe.envs" :key="index"
:label="environment.name"
:value="environment.id"/>
<el-button class="ms-scenario-button" v-if="isShowConfirmButton(pe.id)" size="mini" type="primary"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
<div v-for="pe in data" :key="pe.id" style="margin-left: 20px">
<el-select
v-model="pe['selectEnv']"
:placeholder="$t('workspace.env_group.please_select_env')"
style="margin-top: 8px; width: 200px"
size="small"
>
<el-option
v-for="(environment, index) in pe.envs"
:key="index"
:label="environment.name"
:value="environment.id"
/>
<el-button
class="ms-scenario-button"
v-if="isShowConfirmButton(pe.id)"
size="mini"
type="primary"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])"
>
{{ $t('api_test.environment.environment_config') }}
</el-button>
<template v-slot:empty>
<div v-if="isShowConfirmButton(pe.id)" class="empty-environment">
<el-button class="ms-scenario-button" size="mini" type="primary"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
<el-button
class="ms-scenario-button"
size="mini"
type="primary"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])"
>
{{ $t('api_test.environment.environment_config') }}
</el-button>
</div>
@ -24,24 +40,33 @@
</span>
</div>
<el-button type="primary" @click="handleConfirm" size="small" :style="btnStyle" class="env-confirm">
<el-button
type="primary"
@click="handleConfirm"
size="small"
:style="btnStyle"
class="env-confirm"
>
{{ $t('workspace.env_group.confirm') }}
</el-button>
<!-- 环境配置 -->
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/>
<api-environment-config
ref="environmentConfig"
@close="environmentConfigClose"
/>
</div>
</template>
<script>
import {parseEnvironment} from "@/business/environment/model/EnvironmentModel";
import ApiEnvironmentConfig from "metersphere-frontend/src/components/environment/ApiEnvironmentConfig";
import {getEnvironmentByProjectId} from "metersphere-frontend/src/api/environment";
import {getOwnerProjectIds} from "@/api/project";
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
import ApiEnvironmentConfig from 'metersphere-frontend/src/components/environment/ApiEnvironmentConfig';
import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
import { getOwnerProjectIds } from '@/api/project';
export default {
name: "EnvironmentSelect",
components: {ApiEnvironmentConfig},
name: 'EnvironmentSelect',
components: { ApiEnvironmentConfig },
props: {
envMap: Map,
projectIds: Set,
@ -50,20 +75,20 @@ export default {
type: Boolean,
default() {
return true;
}
},
},
result: {
type: Object,
default() {
return {loading: false}
}
return { loading: false };
},
},
btnStyle: {
type: Object,
default() {
return {width: "360px"}
}
}
return { width: '360px' };
},
},
},
data() {
return {
@ -73,7 +98,7 @@ export default {
permissionProjectIds: [],
dialogVisible: false,
isFullUrl: true,
}
};
},
methods: {
isShowConfirmButton(projectId) {
@ -98,38 +123,39 @@ export default {
}
let arr = [];
this.projectIds.forEach(id => {
const project = this.projectList.find(p => p.id === id);
this.projectIds.forEach((id) => {
const project = this.projectList.find((p) => p.id === id);
if (project) {
let item = {id: id, envs: [], selectEnv: ""};
let item = { id: id, envs: [], selectEnv: '' };
this.data.push(item);
let p = new Promise(resolve => {
getEnvironmentByProjectId(id).then(res => {
let p = new Promise((resolve) => {
getEnvironmentByProjectId(id).then((res) => {
let envs = res.data;
envs.forEach(environment => {
envs.forEach((environment) => {
parseEnvironment(environment);
});
//
let temp = this.data.find(dt => dt.id === id);
let temp = this.data.find((dt) => dt.id === id);
temp.envs = envs;
let envId = undefined;
if (this.envMap) {
envId = this.envMap.get(id);
}
//
temp.selectEnv = envs.filter(e => e.id === envId).length === 0 ? null : envId;
temp.selectEnv =
envs.filter((e) => e.id === envId).length === 0 ? null : envId;
resolve();
})
})
});
});
arr.push(p);
}
})
});
return arr;
},
getUserPermissionProjectIds() {
getOwnerProjectIds().then(res => {
getOwnerProjectIds().then((res) => {
this.permissionProjectIds = res.data;
})
});
},
open() {
this.data = [];
@ -142,8 +168,8 @@ export default {
return Promise.all(this.init());
},
getProjectName(id) {
const project = this.projectList.find(p => p.id === id);
return project ? project.name : "";
const project = this.projectList.find((p) => p.id === id);
return project ? project.name : '';
},
openEnvironmentConfig(projectId, envId) {
if (!projectId) {
@ -155,15 +181,17 @@ export default {
handleConfirm() {
let map = new Map();
let sign = true;
this.data.forEach(dt => {
this.data.forEach((dt) => {
if (!dt.selectEnv) {
sign = false;
return;
}
map.set(dt.id, dt.selectEnv);
})
});
if (!sign) {
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
this.$warning(
this.$t('workspace.env_group.please_select_env_for_current_scenario')
);
return;
}
this.$emit('setProjectEnvMap', map);
@ -176,35 +204,37 @@ export default {
return true;
}
if (this.data.length > 0) {
this.data.forEach(dt => {
this.data.forEach((dt) => {
if (!dt.selectEnv) {
sign = false;
return false;
}
})
});
} else {
//
if (this.envMap && this.envMap.size > 0) {
this.projectIds.forEach(id => {
this.projectIds.forEach((id) => {
if (!this.envMap.get(id)) {
sign = false;
return false;
}
})
});
}
}
if (!sign) {
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
this.$warning(
this.$t('workspace.env_group.please_select_env_for_current_scenario')
);
return false;
}
return true;
},
environmentConfigClose() {
// todo
}
}
}
},
},
};
</script>
<style scoped>

View File

@ -1,24 +1,61 @@
<template>
<div>
<div v-for="(pe, pIndex) in eventData" :key="pe.id">
<el-card shadow="never" style="margin-top: 8px;background: #F5F6F7;border-radius: 4px;">
<i @click="expandCard(pIndex)" v-if="pe.expendStatus==='close'" class="el-icon-caret-right" style="color: var(--primary_color)"/>
<i @click="expandCard(pIndex)" v-else class="el-icon-caret-bottom" style="color: var(--primary_color)"/>
<el-card
shadow="never"
style="margin-top: 8px; background: #f5f6f7; border-radius: 4px"
>
<i
@click="expandCard(pIndex)"
v-if="pe.expendStatus === 'close'"
class="el-icon-caret-right"
style="color: var(--primary_color)"
/>
<i
@click="expandCard(pIndex)"
v-else
class="el-icon-caret-bottom"
style="color: var(--primary_color)"
/>
<span class="project-name" :title="getProjectName(pe.id)">
{{ getProjectName(pe.id) }}
</span><br/>
<div v-if="pe.expendStatus==='open'">
<el-radio-group v-model="pe.envRadio" style="width: 100%;" @change="envRadioChange(pe.envRadio,pIndex)" class="radio-change">
<el-radio label="DEFAULT_ENV" style="margin-top: 7px">{{$t('api_test.environment.default_environment') }}</el-radio>
<el-radio label="CUSTOMIZE_ENV" style="margin-top: 7px">{{$t('api_test.environment.choose_new_environment')}}</el-radio>
{{ getProjectName(pe.id) }} </span
><br />
<div v-if="pe.expendStatus === 'open'">
<el-radio-group
v-model="pe.envRadio"
style="width: 100%"
@change="envRadioChange(pe.envRadio, pIndex)"
class="radio-change"
>
<el-radio label="DEFAULT_ENV" style="margin-top: 7px">{{
$t('api_test.environment.default_environment')
}}</el-radio>
<el-radio label="CUSTOMIZE_ENV" style="margin-top: 7px">{{
$t('api_test.environment.choose_new_environment')
}}</el-radio>
</el-radio-group>
<el-tag v-show="!pe.showEnvSelect" v-for="(itemName,index) in selectedEnvName.get(pe.id)" :key="index" size="mini"
style="margin-left: 0; margin-right: 2px;margin-top: 8px">{{ itemName }}</el-tag>
<el-select v-show="pe.showEnvSelect" v-model="pe['selectEnv']" :placeholder="$t('api_test.environment.select_environment')"
style="margin-top: 8px;width: 100%;" size="small" @change="chooseEnv">
<el-option v-for="(environment, index) in pe.envs" :key="index"
:label="environment.name"
:value="environment.id"/>
<el-tag
v-show="!pe.showEnvSelect"
v-for="(itemName, index) in selectedEnvName.get(pe.id)"
:key="index"
size="mini"
style="margin-left: 0; margin-right: 2px; margin-top: 8px"
>{{ itemName }}</el-tag
>
<el-select
v-show="pe.showEnvSelect"
v-model="pe['selectEnv']"
:placeholder="$t('api_test.environment.select_environment')"
style="margin-top: 8px; width: 100%"
size="small"
@change="chooseEnv"
>
<el-option
v-for="(environment, index) in pe.envs"
:key="index"
:label="environment.name"
:value="environment.id"
/>
</el-select>
</div>
</el-card>
@ -26,155 +63,157 @@
</div>
</template>
<script>
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
import {environmentGetALL} from "metersphere-frontend/src/api/environment";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {parseEnvironment} from "metersphere-frontend/src/model/EnvironmentModel";
import {getEnvironmentByProjectId} from "@/api/api-environment";
import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
import { environmentGetALL } from 'metersphere-frontend/src/api/environment';
import MsTag from 'metersphere-frontend/src/components/MsTag';
import { parseEnvironment } from 'metersphere-frontend/src/model/EnvironmentModel';
import { getEnvironmentByProjectId } from '@/api/api-environment';
export default {
name: "EnvSelectPopover",
components: {MsTag},
data(){
name: 'EnvSelectPopover',
components: { MsTag },
data() {
return {
radio:ENV_TYPE.JSON,
radio: ENV_TYPE.JSON,
visible: false,
groups:[],
selectedEnvName:new Map(),
showEnvName:false,
eventData:[],
evnList:[],
selectEnvMap:new Map(),
}
groups: [],
selectedEnvName: new Map(),
showEnvName: false,
eventData: [],
evnList: [],
selectEnvMap: new Map(),
};
},
computed: {
ENV_TYPE() {
return ENV_TYPE;
}
},
},
props:{
props: {
projectIds: Set,
projectList:Array,
projectEnvMap:Object,
caseIdEnvNameMap:Object,
projectList: Array,
projectEnvMap: Object,
caseIdEnvNameMap: Object,
envMap: Map,
groupId: {
type: String,
default() {
return "";
}
return '';
},
},
isScenario: {
type: Boolean,
default: true
}
default: true,
},
},
methods: {
open(){
open() {
this.initDefaultEnv();
this.getgroups();
},
radioChange(val){
radioChange(val) {
this.radio = val;
},
getProjectName(id) {
const project = this.projectList.find(p => p.id === id);
return project ? project.name : "";
const project = this.projectList.find((p) => p.id === id);
return project ? project.name : '';
},
envRadioChange(val,index){
this.eventData[index].envRadio = val
this.eventData[index].showEnvSelect = this.eventData[index].envRadio === "CUSTOMIZE_ENV";
envRadioChange(val, index) {
this.eventData[index].envRadio = val;
this.eventData[index].showEnvSelect =
this.eventData[index].envRadio === 'CUSTOMIZE_ENV';
},
viewGroup() {
this.visible = true;
},
getgroups(){
environmentGetALL().then(res => {
getgroups() {
environmentGetALL().then((res) => {
let data = res.data;
this.groups = data ? data : [];
})
});
},
chooseEnv(val){
let filter = this.evnList.filter(e => e.id === val);
this.selectEnvMap.set(filter[0].projectId,val);
chooseEnv(val) {
let filter = this.evnList.filter((e) => e.id === val);
this.selectEnvMap.set(filter[0].projectId, val);
this.$emit('setProjectEnvMap', this.selectEnvMap);
},
initDefaultEnv(){
initDefaultEnv() {
this.selectedEnvName = new Map();
this.evnList = [];
this.projectIds.forEach(d => {
let item = {id: d, envs: [], selectEnv: "",envRadio:"DEFAULT_ENV",showEnvSelect:false,expendStatus:"open"};
this.projectIds.forEach((d) => {
let item = {
id: d,
envs: [],
selectEnv: '',
envRadio: 'DEFAULT_ENV',
showEnvSelect: false,
expendStatus: 'open',
};
this.eventData.push(item);
getEnvironmentByProjectId(d)
.then(res => {
let envs = res.data;
envs.forEach(environment => {
parseEnvironment(environment);
});
//
let temp = this.eventData.find(dt => dt.id === d);
temp.envs = envs;
envs.forEach(t=>{
this.evnList.push(t);
})
if (this.envMap && this.envMap.size > 0) {
let envId = this.envMap.get(id);
//
temp.selectEnv = envs.filter(e => e.id === envId).length === 0 ? null : envId;
}
if (this.isScenario){
if (this.projectEnvMap) {
let projectEnvMapElement = this.projectEnvMap[d];
if (projectEnvMapElement.length>0) {
projectEnvMapElement.forEach(envId => {
let filter = envs.filter(e => e.id === envId);
if (!this.selectedEnvName.has(d)) {
let name = [];
name.push(filter[0].name)
this.selectedEnvName.set(d,name);
} else {
this.selectedEnvName.get(d).push(filter[0].name);
}
});
}
}
} else {
if (this.caseIdEnvNameMap) {
let envName = new Set();
for (let key in this.caseIdEnvNameMap) {
envName.add(this.caseIdEnvNameMap[key])
}
this.selectedEnvName.set(d,envName);
}
}
getEnvironmentByProjectId(d).then((res) => {
let envs = res.data;
envs.forEach((environment) => {
parseEnvironment(environment);
});
})
//
let temp = this.eventData.find((dt) => dt.id === d);
temp.envs = envs;
envs.forEach((t) => {
this.evnList.push(t);
});
if (this.envMap && this.envMap.size > 0) {
let envId = this.envMap.get(id);
//
temp.selectEnv =
envs.filter((e) => e.id === envId).length === 0 ? null : envId;
}
if (this.isScenario) {
if (this.projectEnvMap) {
let projectEnvMapElement = this.projectEnvMap[d];
if (projectEnvMapElement.length > 0) {
projectEnvMapElement.forEach((envId) => {
let filter = envs.filter((e) => e.id === envId);
if (!this.selectedEnvName.has(d)) {
let name = [];
name.push(filter[0].name);
this.selectedEnvName.set(d, name);
} else {
this.selectedEnvName.get(d).push(filter[0].name);
}
});
}
}
} else {
if (this.caseIdEnvNameMap) {
let envName = new Set();
for (let key in this.caseIdEnvNameMap) {
envName.add(this.caseIdEnvNameMap[key]);
}
this.selectedEnvName.set(d, envName);
}
}
});
});
},
expandCard(index){
if (this.eventData[index].expendStatus === "open") {
this.eventData[index].expendStatus = "close"
}else {
this.eventData[index].expendStatus = "open"
expandCard(index) {
if (this.eventData[index].expendStatus === 'open') {
this.eventData[index].expendStatus = 'close';
} else {
this.eventData[index].expendStatus = 'open';
}
}
}
}
},
},
};
</script>
<style scoped>
.mode-span{
.mode-span {
margin-left: 6px;
}
</style>
<style lang="scss" scoped>
<style lang="scss" scoped>
.radio-change:deep(.el-radio__input.is-checked + .el-radio__label) {
color: #606266 !important;
}
</style>

View File

@ -1,8 +1,12 @@
<template>
<ms-container>
<ms-aside-container>
<ms-api-scenario-module @selectModule="selectModule" @getApiModuleTree="initTree"
@refresh="refresh" @saveAsEdit="editScenario"/>
<ms-api-scenario-module
@selectModule="selectModule"
@getApiModuleTree="initTree"
@refresh="refresh"
@saveAsEdit="editScenario"
/>
</ms-aside-container>
<ms-main-container>
<ms-api-scenario-list
@ -11,36 +15,42 @@
@selection="setData"
:referenced="true"
:select-project-id="cuurentProjectId"
ref="apiScenarioList"/>
ref="apiScenarioList"
/>
<el-button style="float: right;margin: 10px" @click="importApiScenario" type="primary">
<el-button
style="float: right; margin: 10px"
@click="importApiScenario"
type="primary"
>
{{ $t('api_test.scenario.reference') }}
</el-button>
<el-button style="float: right;margin: 10px" @click="copyApiScenario">{{ $t('commons.copy') }}</el-button>
<el-button style="float: right; margin: 10px" @click="copyApiScenario">{{
$t('commons.copy')
}}</el-button>
</ms-main-container>
</ms-container>
</template>
<script>
import {getApiScenarios} from "@/api/scenario";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsAsideContainer from "metersphere-frontend/src/components/MsAsideContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import MsApiScenarioList from "@/business/automation/scenario/ApiScenarioList";
import {getUUID} from "metersphere-frontend/src/utils";
import MsApiScenarioModule from "@/business/automation/scenario/ApiScenarioModule";
import MsEditApiScenario from "../scenario/EditApiScenario";
import { getApiScenarios } from '@/api/scenario';
import MsContainer from 'metersphere-frontend/src/components/MsContainer';
import MsAsideContainer from 'metersphere-frontend/src/components/MsAsideContainer';
import MsMainContainer from 'metersphere-frontend/src/components/MsMainContainer';
import MsApiScenarioList from '@/business/automation/scenario/ApiScenarioList';
import { getUUID } from 'metersphere-frontend/src/utils';
import MsApiScenarioModule from '@/business/automation/scenario/ApiScenarioModule';
import MsEditApiScenario from '../scenario/EditApiScenario';
export default {
name: "ImportApiScenario",
name: 'ImportApiScenario',
components: {
MsApiScenarioModule,
MsApiScenarioList,
MsMainContainer,
MsAsideContainer,
MsContainer,
MsEditApiScenario
MsEditApiScenario,
},
comments: {},
data() {
@ -51,15 +61,15 @@ export default {
currentScenario: [],
currentScenarioIds: [],
moduleOptions: {},
cuurentProjectId: "",
cuurentProjectId: '',
scenarioDefinition: Object,
}
};
},
watch: {},
methods: {
setData(data) {
this.currentScenario = Array.from(data).map(row => row);
this.currentScenarioIds = Array.from(data).map(row => row.id);
this.currentScenario = Array.from(data).map((row) => row);
this.currentScenarioIds = Array.from(data).map((row) => row.id);
},
selectModule(data) {
this.currentModule = data;
@ -67,32 +77,43 @@ export default {
importApiScenario() {
let scenarios = [];
if (this.currentScenario) {
this.currentScenario.forEach(item => {
let obj = {id: item.id, name: item.name, type: "scenario", referenced: 'REF', resourceId: getUUID()};
this.currentScenario.forEach((item) => {
let obj = {
id: item.id,
name: item.name,
type: 'scenario',
referenced: 'REF',
resourceId: getUUID(),
};
scenarios.push(obj);
})
});
}
this.$emit('addScenario', scenarios);
},
getApiScenario() {
let scenarios = [];
this.result = getApiScenarios(this.currentScenarioIds).then(response => {
if (response.data) {
response.data.forEach(item => {
let scenarioDefinition = JSON.parse(item.scenarioDefinition);
let obj = {
id: item.id,
name: item.name,
type: "scenario",
referenced: 'Copy',
resourceId: getUUID(),
hashTree: scenarioDefinition && scenarioDefinition.hashTree ? scenarioDefinition.hashTree : []
};
scenarios.push(obj);
})
this.$emit('addScenario', scenarios);
this.result = getApiScenarios(this.currentScenarioIds).then(
(response) => {
if (response.data) {
response.data.forEach((item) => {
let scenarioDefinition = JSON.parse(item.scenarioDefinition);
let obj = {
id: item.id,
name: item.name,
type: 'scenario',
referenced: 'Copy',
resourceId: getUUID(),
hashTree:
scenarioDefinition && scenarioDefinition.hashTree
? scenarioDefinition.hashTree
: [],
};
scenarios.push(obj);
});
this.$emit('addScenario', scenarios);
}
}
})
);
},
copyApiScenario() {
if (this.currentScenarioIds) {
@ -108,13 +129,10 @@ export default {
},
editScenario(row) {
this.currentScenario = row;
this.addTab({name: 'add'});
this.addTab({ name: 'add' });
},
}
}
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -3,8 +3,8 @@
class="el-input-tag input-tag-wrapper"
:class="[size ? 'el-input-tag--' + size : '']"
style="height: auto"
@click="foucusTagInput">
@click="foucusTagInput"
>
<el-tag
class="ms-top"
v-for="(tag, idx) in innerTags"
@ -14,17 +14,19 @@
:size="size"
:closable="!readOnly"
:disable-transitions="false"
@close="remove(idx)">
{{ tag && tag.length > 10 ? tag.substring(0, 10) + "..." : tag }}
@close="remove(idx)"
>
{{ tag && tag.length > 10 ? tag.substring(0, 10) + '...' : tag }}
</el-tag>
<input
:disabled="readOnly"
class="tag-input el-input"
v-model="newTag"
:placeholder=defaultPlaceHolder
:placeholder="defaultPlaceHolder"
@keydown.delete.stop="removeLastTag"
@keydown="addNew"
@blur="addNew"/>
@blur="addNew"
/>
</div>
</template>
@ -39,17 +41,17 @@ export default {
errorInfo: String,
addTagOnKeys: {
type: Array,
default: () => [13, 188, 9]
default: () => [13, 188, 9],
},
readOnly: {
type: Boolean,
default: false
default: false,
},
size: {type: String, default: "small"},
size: { type: String, default: 'small' },
prop: {
type: String,
default: "tags"
}
default: 'tags',
},
},
created() {
if (!this.currentScenario[this.prop]) {
@ -63,8 +65,10 @@ export default {
return {
defaultPlaceHolder: this.$t('commons.tag_tip'),
newTag: '',
innerTags: this.currentScenario[this.prop] ? [...this.currentScenario[this.prop]] : []
}
innerTags: this.currentScenario[this.prop]
? [...this.currentScenario[this.prop]]
: [],
};
},
watch: {
innerTags() {
@ -72,79 +76,82 @@ export default {
},
'currentScenario.tags'() {
if (this.prop === 'tags') {
if (!this.currentScenario[this.prop] || this.currentScenario[this.prop] === '' || this.currentScenario[this.prop].length === 0) {
if (
!this.currentScenario[this.prop] ||
this.currentScenario[this.prop] === '' ||
this.currentScenario[this.prop].length === 0
) {
if (this.innerTags.length !== 0) {
this.innerTags = [];
}
}
}
},
},
methods: {
foucusTagInput() {
if (!this.readOnly && this.$el.querySelector('.tag-input')) {
this.$el.querySelector('.tag-input').focus()
this.$el.querySelector('.tag-input').focus();
}
},
addNew(e) {
if (e && (!this.addTagOnKeys.includes(e.keyCode)) && (e.type !== 'blur')) {
return
if (e && !this.addTagOnKeys.includes(e.keyCode) && e.type !== 'blur') {
return;
}
if (e) {
e.stopPropagation()
e.preventDefault()
e.stopPropagation();
e.preventDefault();
}
let addSuucess = false
let addSuucess = false;
if (this.newTag.includes(',')) {
this.newTag.split(',').forEach(item => {
this.newTag.split(',').forEach((item) => {
if (this.addTag(item.trim())) {
addSuucess = true
addSuucess = true;
}
})
});
} else {
if (this.addTag(this.newTag.trim())) {
addSuucess = true
addSuucess = true;
}
}
if (addSuucess) {
this.tagChange()
this.newTag = ''
this.tagChange();
this.newTag = '';
}
this.$emit("onblur");
this.$emit('onblur');
},
addTag(tag) {
tag = tag.trim()
tag = tag.trim();
if (tag && !this.innerTags.includes(tag)) {
this.innerTags.push(tag)
return true
this.innerTags.push(tag);
return true;
} else {
if (tag !== "" && this.errorInfo) {
if (tag !== '' && this.errorInfo) {
this.$error(this.errorInfo);
}
}
return false
return false;
},
remove(index) {
this.innerTags.splice(index, 1);
this.tagChange();
this.$nextTick(() => {
//tagonblur
this.$emit("onblur");
this.$emit('onblur');
});
},
removeLastTag() {
if (this.newTag) {
return
return;
}
this.innerTags.pop()
this.tagChange()
this.innerTags.pop();
this.tagChange();
},
tagChange() {
this.$emit('input', this.innerTags)
}
}
}
this.$emit('input', this.innerTags);
},
},
};
</script>
<style scoped>
@ -160,7 +167,7 @@ export default {
display: inline-block;
outline: none;
padding: 0 10px 0 5px;
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
width: 100%;
}
@ -173,7 +180,8 @@ export default {
border: 0;
color: #303133;
font-size: 12px;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
Arial, sans-serif;
outline: none;
padding-left: 0;
width: 100px;
@ -198,5 +206,4 @@ export default {
height: 36px;
line-height: 36px;
}
</style>

View File

@ -6,31 +6,31 @@
width="30%"
>
<el-radio-group v-model="deleteCurrentVersion">
<el-radio :label="true">{{ $t('commons.delete_current_version') }}</el-radio>
<el-radio :label="true">{{
$t('commons.delete_current_version')
}}</el-radio>
<el-radio :label="false">{{ $t('commons.delete_all_version') }}</el-radio>
</el-radio-group>
<template v-slot:footer>
<ms-dialog-footer
@cancel="close"
@confirm="handleDelete">
<ms-dialog-footer @cancel="close" @confirm="handleDelete">
</ms-dialog-footer>
</template>
</el-dialog>
</template>
<script>
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
export default {
name: "ListItemDeleteConfirm",
components: {MsDialogFooter},
name: 'ListItemDeleteConfirm',
components: { MsDialogFooter },
data() {
return {
deleteApiVisible: false,
title: null,
deleteCurrentVersion: true,
api: {}
api: {},
};
},
methods: {
@ -46,10 +46,8 @@ export default {
handleDelete() {
this.$emit('handleDelete', this.api, this.deleteCurrentVersion);
},
}
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -5,51 +5,62 @@
<el-icon class="el-icon-more"></el-icon>
</el-link>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="ref">{{ $t('api_test.automation.view_ref') }}</el-dropdown-item>
<el-dropdown-item command="schedule" v-permission="['PROJECT_API_SCENARIO:READ+SCHEDULE']">
<el-dropdown-item command="ref">{{
$t('api_test.automation.view_ref')
}}</el-dropdown-item>
<el-dropdown-item
command="schedule"
v-permission="['PROJECT_API_SCENARIO:READ+SCHEDULE']"
>
{{ $t('api_test.automation.schedule') }}
</el-dropdown-item>
<el-dropdown-item command="create_performance" v-permission="['PROJECT_API_SCENARIO:READ+CREATE_PERFORMANCE']"
v-modules="['performance']">
<el-dropdown-item
command="create_performance"
v-permission="['PROJECT_API_SCENARIO:READ+CREATE_PERFORMANCE']"
v-modules="['performance']"
>
{{ $t('api_test.create_performance_test') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<ms-schedule-maintain ref="scheduleMaintain" @refreshTable="refreshTable" :request="request"/>
<ms-schedule-maintain
ref="scheduleMaintain"
@refreshTable="refreshTable"
:request="request"
/>
</div>
</template>
<script>
import {genPerformanceTestJmx} from "@/api/scenario";
import MsScheduleMaintain from "@/business/automation/schedule/ScheduleMaintain";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {hasPermission} from "metersphere-frontend/src/utils/permission";
import {getUUID} from "metersphere-frontend/src/utils";
import {usePerformanceStore} from "@/store";
import { genPerformanceTestJmx } from '@/api/scenario';
import MsScheduleMaintain from '@/business/automation/schedule/ScheduleMaintain';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { hasPermission } from 'metersphere-frontend/src/utils/permission';
import { getUUID } from 'metersphere-frontend/src/utils';
import { usePerformanceStore } from '@/store';
const performanceStore = usePerformanceStore();
export default {
name: "MsScenarioExtendButtons",
components: {MsScheduleMaintain},
name: 'MsScenarioExtendButtons',
components: { MsScheduleMaintain },
props: {
row: Object,
request: {}
request: {},
},
methods: {
hasPermission,
handleCommand(cmd) {
switch (cmd) {
case "ref":
this.$emit("showCaseRef", this.row);
case 'ref':
this.$emit('showCaseRef', this.row);
break;
case "schedule":
case 'schedule':
this.$emit('openSchedule');
this.$refs.scheduleMaintain.open(this.row);
break;
case "create_performance":
case 'create_performance':
this.createPerformance(this.row);
break;
}
@ -67,7 +78,7 @@ export default {
run.ids = scenarioIds;
run.id = getUUID();
run.name = row.name;
genPerformanceTestJmx(run).then(response => {
genPerformanceTestJmx(run).then((response) => {
let jmxInfo = response.data.jmxInfoDTO;
if (jmxInfo) {
let projectEnvMap = response.data.projectEnvMap;
@ -79,9 +90,9 @@ export default {
jmxObj.scenarioId = row.id;
jmxObj.version = row.version;
jmxObj.projectEnvMap = projectEnvMap;
performanceStore.$patch({'test': {name: row.name, jmx: jmxObj}});
performanceStore.$patch({ test: { name: row.name, jmx: jmxObj } });
this.$router.push({
path: "/performance/test/create"
path: '/performance/test/create',
});
}
});
@ -89,10 +100,8 @@ export default {
openScenario(item) {
this.$emit('openScenario', item);
},
refreshTable() {
},
}
refreshTable() {},
},
};
</script>

View File

@ -9,8 +9,8 @@ import {
PRIORITY,
STEP_COUNT,
TAGS,
UPDATE_TIME
} from "metersphere-frontend/src/components/search/search-components";
UPDATE_TIME,
} from 'metersphere-frontend/src/components/search/search-components';
export function STEP() {
let map = new Map([
@ -37,12 +37,65 @@ export function STEP() {
['CustomizeReq', getDefaultSamplerMenu()],
['MaxSamplerProxy', getDefaultSamplerMenu()],
['GenericController', getAll()],
['SpecialSteps', ['HTTPSamplerProxy', 'Assertions', 'DubboSampler', 'JDBCSampler', 'TCPSampler', 'Sampler', 'AbstractSampler', 'JSR223Processor', 'API', 'MsUiCommand']],
['AllSamplerProxy', ['GenericController', 'HTTPSamplerProxy', 'DubboSampler', 'JDBCSampler', 'TCPSampler', 'Sampler', 'AbstractSampler', 'JSR223Processor', 'API', 'MsUiCommand']],
['DEFINITION', ['HTTPSamplerProxy', 'DubboSampler', 'JDBCSampler', 'TCPSampler']],
['ALlSamplerStep', ['JSR223PreProcessor', 'JSR223PostProcessor', 'JDBCPreProcessor', 'JDBCPostProcessor', 'Assertions', 'Extract', 'ConstantTimer']],
['AllCanExecType', ['HTTPSamplerProxy', 'DubboSampler', 'JDBCSampler', 'TCPSampler', 'JSR223Processor', 'AbstractSampler']]]);
return map
[
'SpecialSteps',
[
'HTTPSamplerProxy',
'Assertions',
'DubboSampler',
'JDBCSampler',
'TCPSampler',
'Sampler',
'AbstractSampler',
'JSR223Processor',
'API',
'MsUiCommand',
],
],
[
'AllSamplerProxy',
[
'GenericController',
'HTTPSamplerProxy',
'DubboSampler',
'JDBCSampler',
'TCPSampler',
'Sampler',
'AbstractSampler',
'JSR223Processor',
'API',
'MsUiCommand',
],
],
[
'DEFINITION',
['HTTPSamplerProxy', 'DubboSampler', 'JDBCSampler', 'TCPSampler'],
],
[
'ALlSamplerStep',
[
'JSR223PreProcessor',
'JSR223PostProcessor',
'JDBCPreProcessor',
'JDBCPostProcessor',
'Assertions',
'Extract',
'ConstantTimer',
],
],
[
'AllCanExecType',
[
'HTTPSamplerProxy',
'DubboSampler',
'JDBCSampler',
'TCPSampler',
'JSR223Processor',
'AbstractSampler',
],
],
]);
return map;
}
export const ELEMENT_TYPE = {
@ -61,48 +114,274 @@ export const ELEMENT_TYPE = {
Extract: 'Extract',
CustomizeReq: 'CustomizeReq',
LoopController: 'LoopController',
Plugin: 'Plugin'
}
Plugin: 'Plugin',
};
export const TYPE_TO_C = new Map([
['scenario', 'io.metersphere.api.dto.definition.request.MsScenario'],
['UiScenario', 'io.metersphere.xpack.ui.hashtree.MsUiScenario'],
['HTTPSamplerProxy', 'io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy'],
['DubboSampler', 'io.metersphere.api.dto.definition.request.sampler.MsDubboSampler'],
['JDBCSampler', 'io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler'],
['TCPSampler', 'io.metersphere.api.dto.definition.request.sampler.MsTCPSampler'],
['IfController', 'io.metersphere.api.dto.definition.request.controller.MsIfController'],
['TransactionController', 'io.metersphere.api.dto.definition.request.controller.MsTransactionController'],
['LoopController', 'io.metersphere.api.dto.definition.request.controller.MsLoopController'],
['ConstantTimer', 'io.metersphere.api.dto.definition.request.timer.MsConstantTimer'],
['JSR223Processor', 'io.metersphere.api.dto.definition.request.processors.MsJSR223Processor'],
['JSR223PreProcessor', 'io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor'],
['JSR223PostProcessor', 'io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor'],
['JDBCPreProcessor', 'io.metersphere.api.dto.definition.request.processors.pre.MsJDBCPreProcessor'],
['JDBCPostProcessor', 'io.metersphere.api.dto.definition.request.processors.post.MsJDBCPostProcessor'],
['Assertions', 'io.metersphere.api.dto.definition.request.assertions.MsAssertions'],
[
'HTTPSamplerProxy',
'io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy',
],
[
'DubboSampler',
'io.metersphere.api.dto.definition.request.sampler.MsDubboSampler',
],
[
'JDBCSampler',
'io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler',
],
[
'TCPSampler',
'io.metersphere.api.dto.definition.request.sampler.MsTCPSampler',
],
[
'IfController',
'io.metersphere.api.dto.definition.request.controller.MsIfController',
],
[
'TransactionController',
'io.metersphere.api.dto.definition.request.controller.MsTransactionController',
],
[
'LoopController',
'io.metersphere.api.dto.definition.request.controller.MsLoopController',
],
[
'ConstantTimer',
'io.metersphere.api.dto.definition.request.timer.MsConstantTimer',
],
[
'JSR223Processor',
'io.metersphere.api.dto.definition.request.processors.MsJSR223Processor',
],
[
'JSR223PreProcessor',
'io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor',
],
[
'JSR223PostProcessor',
'io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor',
],
[
'JDBCPreProcessor',
'io.metersphere.api.dto.definition.request.processors.pre.MsJDBCPreProcessor',
],
[
'JDBCPostProcessor',
'io.metersphere.api.dto.definition.request.processors.post.MsJDBCPostProcessor',
],
[
'Assertions',
'io.metersphere.api.dto.definition.request.assertions.MsAssertions',
],
['Extract', 'io.metersphere.api.dto.definition.request.extract.MsExtract'],
['JmeterElement', 'io.metersphere.api.dto.definition.request.unknown.MsJmeterElement'],
[
'JmeterElement',
'io.metersphere.api.dto.definition.request.unknown.MsJmeterElement',
],
['TestPlan', 'io.metersphere.api.dto.definition.request.MsTestPlan'],
['ThreadGroup', 'io.metersphere.api.dto.definition.request.MsThreadGroup'],
['DNSCacheManager', 'io.metersphere.api.dto.definition.request.dns.MsDNSCacheManager'],
['DebugSampler', 'io.metersphere.api.dto.definition.request.sampler.MsDebugSampler'],
['AuthManager', 'io.metersphere.api.dto.definition.request.auth.MsAuthManager']
])
[
'DNSCacheManager',
'io.metersphere.api.dto.definition.request.dns.MsDNSCacheManager',
],
[
'DebugSampler',
'io.metersphere.api.dto.definition.request.sampler.MsDebugSampler',
],
[
'AuthManager',
'io.metersphere.api.dto.definition.request.auth.MsAuthManager',
],
]);
export const PLUGIN_ELEMENTS = new Map([
['menu_post_processors', ['HtmlExtractor', 'JMESPathExtractor', 'JSONPostProcessor', 'RegexExtractor', 'BoundaryExtractor', 'Separator', 'XPath2Extractor', 'XPathExtractor', 'ResultAction', 'DebugPostProcessor', 'BeanShellPostProcessor']],
['menu_assertions', ['JSONPathAssertion', 'SizeAssertion', 'JSR223Assertion', 'XPath2Assertion', 'Separator', 'HTMLAssertion', 'JMESPathAssertion', 'MD5HexAssertion', 'SMIMEAssertion', 'XMLSchemaAssertion', 'XMLAssertion', 'XPathAssertion', 'DurationAssertion', 'CompareAssertion', 'BeanShellAssertion']],
['menu_listener', ['AbstractVisualizer', 'AbstractListener', 'ViewResultsFullVisualizer', 'SummaryReport', 'StatVisualizer', 'BackendListener', 'Separator', 'JSR223Listener', 'ResultSaver', 'RespTimeGraphVisualizer', 'GraphVisualizer', 'AssertionVisualizer', 'ComparisonVisualizer', 'StatGraphVisualizer', 'Summariser', 'TableVisualizer', 'SimpleDataWriter', 'MailerVisualizer', 'BeanShellListener']],
['menu_pre_processors', ['AbstractPostProcessor', 'UserParameters', 'Separator', 'AnchorModifier', 'URLRewritingModifier', 'SampleTimeout', 'RegExUserParameters', 'BeanShellPreProcessor']],
['menu_logic_controller', ['GenericController', 'scenario', 'IfController', 'LoopController', 'IfControllerPanel', 'TransactionController', 'LoopControlPanel', 'WhileController', 'Separator', 'ForeachControlPanel', 'IncludeController', 'RunTime', 'CriticalSectionController', 'InterleaveControl', 'OnceOnlyController', 'RecordController', 'LogicController', 'RandomControl', 'RandomOrderController', 'ThroughputController', 'SwitchController', 'ModuleController']],
[
'menu_post_processors',
[
'HtmlExtractor',
'JMESPathExtractor',
'JSONPostProcessor',
'RegexExtractor',
'BoundaryExtractor',
'Separator',
'XPath2Extractor',
'XPathExtractor',
'ResultAction',
'DebugPostProcessor',
'BeanShellPostProcessor',
],
],
[
'menu_assertions',
[
'JSONPathAssertion',
'SizeAssertion',
'JSR223Assertion',
'XPath2Assertion',
'Separator',
'HTMLAssertion',
'JMESPathAssertion',
'MD5HexAssertion',
'SMIMEAssertion',
'XMLSchemaAssertion',
'XMLAssertion',
'XPathAssertion',
'DurationAssertion',
'CompareAssertion',
'BeanShellAssertion',
],
],
[
'menu_listener',
[
'AbstractVisualizer',
'AbstractListener',
'ViewResultsFullVisualizer',
'SummaryReport',
'StatVisualizer',
'BackendListener',
'Separator',
'JSR223Listener',
'ResultSaver',
'RespTimeGraphVisualizer',
'GraphVisualizer',
'AssertionVisualizer',
'ComparisonVisualizer',
'StatGraphVisualizer',
'Summariser',
'TableVisualizer',
'SimpleDataWriter',
'MailerVisualizer',
'BeanShellListener',
],
],
[
'menu_pre_processors',
[
'AbstractPostProcessor',
'UserParameters',
'Separator',
'AnchorModifier',
'URLRewritingModifier',
'SampleTimeout',
'RegExUserParameters',
'BeanShellPreProcessor',
],
],
[
'menu_logic_controller',
[
'GenericController',
'scenario',
'IfController',
'LoopController',
'IfControllerPanel',
'TransactionController',
'LoopControlPanel',
'WhileController',
'Separator',
'ForeachControlPanel',
'IncludeController',
'RunTime',
'CriticalSectionController',
'InterleaveControl',
'OnceOnlyController',
'RecordController',
'LogicController',
'RandomControl',
'RandomOrderController',
'ThroughputController',
'SwitchController',
'ModuleController',
],
],
['menu_fragments', ['TestFragmentController']],
['menu_non_test_elements', ['ProxyControl', 'HttpMirrorControl', 'GenerateTree', 'PropertyControl']],
['menu_generative_controller', ['HTTPSamplerProxy', 'JSR223Processor', 'DubboSampler', 'JDBCSampler', 'TCPSampler', 'Sampler', 'AbstractSampler', 'CustomizeReq', 'HttpTestSample', 'TestAction', 'DebugSampler', 'JSR223Sampler', 'Separator', 'AjpSampler', 'AccessLogSampler', 'BeanShellSampler', 'BoltSampler', 'FtpTestSampler', 'GraphQLHTTPSampler', 'JDBCSampler', 'JMSPublisher', 'JMSSampler', 'JMSSubscriber', 'JUnitTestSampler', 'JavaTestSampler', 'LdapExtTestSampler', 'LdapTestSampler', 'SystemSampler', 'SmtpSampler', 'TCPSampler', 'MailReaderSampler']],
[
'menu_non_test_elements',
['ProxyControl', 'HttpMirrorControl', 'GenerateTree', 'PropertyControl'],
],
[
'menu_generative_controller',
[
'HTTPSamplerProxy',
'JSR223Processor',
'DubboSampler',
'JDBCSampler',
'TCPSampler',
'Sampler',
'AbstractSampler',
'CustomizeReq',
'HttpTestSample',
'TestAction',
'DebugSampler',
'JSR223Sampler',
'Separator',
'AjpSampler',
'AccessLogSampler',
'BeanShellSampler',
'BoltSampler',
'FtpTestSampler',
'GraphQLHTTPSampler',
'JDBCSampler',
'JMSPublisher',
'JMSSampler',
'JMSSubscriber',
'JUnitTestSampler',
'JavaTestSampler',
'LdapExtTestSampler',
'LdapTestSampler',
'SystemSampler',
'SmtpSampler',
'TCPSampler',
'MailReaderSampler',
],
],
['menu_threads', ['SetupThreadGroup', 'PostThreadGroup', 'ThreadGroup']],
['menu_timer', ['ConstantTimer', 'UniformRandomTimer', 'PreciseThroughputTimer', 'ConstantThroughputTimer', 'Separator', 'JSR223Timer', 'SyncTimer', 'PoissonRandomTimer', 'GaussianRandomTimer', 'BeanShellTimer']],
['menu_config_element', ['CSVDataSet', 'HeaderPanel', 'CookiePanel', 'CacheManager', 'HttpDefaults', 'Separator', 'BoltConnectionElement', 'DNSCachePanel', 'FtpConfig', 'AuthPanel', 'DataSourceElement', 'JavaConfig', 'LdapExtConfig', 'LdapConfig', 'TCPConfig', 'KeystoreConfig', 'ArgumentsPanel', 'LoginConfig', 'SimpleConfig', 'CounterConfig', 'RandomVariableConfig']],
])
[
'menu_timer',
[
'ConstantTimer',
'UniformRandomTimer',
'PreciseThroughputTimer',
'ConstantThroughputTimer',
'Separator',
'JSR223Timer',
'SyncTimer',
'PoissonRandomTimer',
'GaussianRandomTimer',
'BeanShellTimer',
],
],
[
'menu_config_element',
[
'CSVDataSet',
'HeaderPanel',
'CookiePanel',
'CacheManager',
'HttpDefaults',
'Separator',
'BoltConnectionElement',
'DNSCachePanel',
'FtpConfig',
'AuthPanel',
'DataSourceElement',
'JavaConfig',
'LdapExtConfig',
'LdapConfig',
'TCPConfig',
'KeystoreConfig',
'ArgumentsPanel',
'LoginConfig',
'SimpleConfig',
'CounterConfig',
'RandomVariableConfig',
],
],
]);
export function getDefaultSamplerMenu() {
let array = [];
@ -118,7 +397,12 @@ export function init() {
let allArray = [];
allArray = allArray.concat(PLUGIN_ELEMENTS.get('menu_generative_controller'));
allArray = allArray.concat(PLUGIN_ELEMENTS.get('menu_logic_controller'));
allArray = allArray.concat(['scenario', 'ConstantTimer', 'JSR223Processor', 'Assertions']);
allArray = allArray.concat([
'scenario',
'ConstantTimer',
'JSR223Processor',
'Assertions',
]);
return allArray;
}
@ -135,42 +419,55 @@ export function getAll() {
function _getModuleTree(options) {
return {
key: "moduleIds",
key: 'moduleIds',
name: 'MsTableSearchNodeTree',
label: "test_track.case.module",
label: 'test_track.case.module',
operator: {
value: OPERATORS.IN.value,
options: [OPERATORS.IN, OPERATORS.NOT_IN]
options: [OPERATORS.IN, OPERATORS.NOT_IN],
},
options: options,
init: undefined // 高级搜索框非首次打开时会执行该函数在组件首次created时给其赋值
}
init: undefined, // 高级搜索框非首次打开时会执行该函数在组件首次created时给其赋值
};
}
export const SCENARIO_MODULE_TREE = _getModuleTree({
url: "/api/automation/module/list",
type: "GET",
params: {}
})
url: '/api/automation/module/list',
type: 'GET',
params: {},
});
export const SCENARIO_MODULE_TRASH_TREE = _getModuleTree({
url: "/api/automation/module/trash/list",
type: "GET",
params: {}
})
url: '/api/automation/module/trash/list',
type: 'GET',
params: {},
});
export const API_STATUS_TRASH = {
key: "status",
key: 'status',
name: 'MsTableSearchSelect',
label: 'commons.status',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
options: [OPERATORS.IN, OPERATORS.NOT_IN],
},
options: [{value: 'Trash', label: 'test_track.plan.plan_status_trash'}],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_SCENARIO_CONFIGS_TRASH = [ID, NAME, PRIORITY, TAGS, API_SCENARIO_RESULT, UPDATE_TIME, CREATE_TIME, CREATOR, FOLLOW_PEOPLE, STEP_COUNT, SCENARIO_MODULE_TRASH_TREE, API_STATUS_TRASH];
options: [{ value: 'Trash', label: 'test_track.plan.plan_status_trash' }],
props: {
// 尾部控件的props一般为element ui控件的props
multiple: true,
},
};
export const API_SCENARIO_CONFIGS_TRASH = [
ID,
NAME,
PRIORITY,
TAGS,
API_SCENARIO_RESULT,
UPDATE_TIME,
CREATE_TIME,
CREATOR,
FOLLOW_PEOPLE,
STEP_COUNT,
SCENARIO_MODULE_TRASH_TREE,
API_STATUS_TRASH,
];

View File

@ -2,11 +2,15 @@
<div v-if="isShow" style="float: right">
<el-dropdown placement="bottom" trigger="click" size="medium">
<div @click.stop class="show-more-btn">
<i class="el-icon-more ms-icon-more"/>
<i class="el-icon-more ms-icon-more" />
</div>
<el-dropdown-menu slot="dropdown" class="dropdown-menu-class">
<el-dropdown-item v-for="(btn,index) in buttons" :key="index" @click.native.stop="click(btn)">
{{btn.name}}
<el-dropdown-item
v-for="(btn, index) in buttons"
:key="index"
@click.native.stop="click(btn)"
>
{{ btn.name }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -14,47 +18,47 @@
</template>
<script>
export default {
name: "ShowMoreBtn",
props: {
isShow: {
type: Boolean,
default: false
},
buttons: Array,
row: Object,
size: Number
export default {
name: 'ShowMoreBtn',
props: {
isShow: {
type: Boolean,
default: false,
},
methods: {
click(btn) {
if (btn.handleClick instanceof Function) {
btn.handleClick(this.row);
}
buttons: Array,
row: Object,
size: Number,
},
methods: {
click(btn) {
if (btn.handleClick instanceof Function) {
btn.handleClick(this.row);
}
}
}
},
},
};
</script>
<style scoped>
.ms-icon-more {
margin-top: 15px;
transform: rotate(0deg);
}
.ms-icon-more {
margin-top: 15px;
transform: rotate(0deg);
}
.show-more-btn {
width: 20px;
height: 25px;
line-height: 25px;
}
.show-more-btn {
width: 20px;
height: 25px;
line-height: 25px;
}
.show-more-btn-title {
color: #696969;
background-color: #e2e2e2;
padding: 5px;
}
.show-more-btn-title {
color: #696969;
background-color: #e2e2e2;
padding: 5px;
}
.dropdown-menu-class {
padding: 1px 0;
text-align: center;
}
.dropdown-menu-class {
padding: 1px 0;
text-align: center;
}
</style>

View File

@ -1,34 +1,50 @@
<template>
<el-dialog :close-on-click-modal="false" :title="$t('api_test.definition.request.save_as_case')"
:visible.sync="httpVisible"
width="30%"
:destroy-on-close="true" append-to-body>
<el-form :model="httpForm" label-position="right" label-width="80px" size="small" :rules="rule" ref="httpForm"
v-if="!loading">
<el-dialog
:close-on-click-modal="false"
:title="$t('api_test.definition.request.save_as_case')"
:visible.sync="httpVisible"
width="30%"
:destroy-on-close="true"
append-to-body
>
<el-form
:model="httpForm"
label-position="right"
label-width="80px"
size="small"
:rules="rule"
ref="httpForm"
v-if="!loading"
>
<el-form-item :label="$t('api_definition.case_name')" prop="name">
<el-input v-model="httpForm.name" autocomplete="off" :placeholder="$t('api_definition.case_name')"
show-word-limit maxlength="100"/>
<el-input
v-model="httpForm.name"
autocomplete="off"
:placeholder="$t('api_definition.case_name')"
show-word-limit
maxlength="100"
/>
</el-form-item>
</el-form>
<template v-slot:footer>
<ms-dialog-footer
@cancel="httpVisible = false"
@confirm="saveApi" v-prevent-re-click>
@confirm="saveApi"
v-prevent-re-click
>
</ms-dialog-footer>
</template>
</el-dialog>
</template>
<script>
import {createApiCase} from "@/api/api-test-case";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import {getUUID} from "metersphere-frontend/src/utils";
import { createApiCase } from '@/api/api-test-case';
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
import { getUUID } from 'metersphere-frontend/src/utils';
export default {
name: "MsAddApiCase",
components: {MsDialogFooter},
name: 'MsAddApiCase',
components: { MsDialogFooter },
data() {
return {
httpVisible: false,
@ -36,13 +52,20 @@ export default {
httpForm: {},
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 100, message: this.$t('test_track.length_less_than') + '100', trigger: 'blur'}
{
required: true,
message: this.$t('test_track.case.input_name'),
trigger: 'blur',
},
{
max: 100,
message: this.$t('test_track.length_less_than') + '100',
trigger: 'blur',
},
],
},
}
}
,
};
},
methods: {
saveApi() {
this.$refs.httpForm.validate(async (valid) => {
@ -58,7 +81,7 @@ export default {
priority: 'P0',
active: true,
uuid: getUUID(),
request: api
request: api,
};
obj.projectId = api.projectId;
obj.id = obj.uuid;
@ -75,9 +98,9 @@ export default {
let request = data.request;
if (request.body) {
if (request.body.kvs) {
request.body.kvs.forEach(param => {
request.body.kvs.forEach((param) => {
if (param.files) {
param.files.forEach(item => {
param.files.forEach((item) => {
if (item.file) {
let fileId = getUUID().substring(0, 8);
item.name = item.file.name;
@ -90,9 +113,9 @@ export default {
});
}
if (request.body.binary) {
request.body.binary.forEach(param => {
request.body.binary.forEach((param) => {
if (param.files) {
param.files.forEach(item => {
param.files.forEach((item) => {
if (item.file) {
let fileId = getUUID().substring(0, 8);
item.name = item.file.name;
@ -108,10 +131,10 @@ export default {
return bodyUploadFiles;
},
reload() {
this.loading = true
this.loading = true;
this.$nextTick(() => {
this.loading = false
})
this.loading = false;
});
},
open(api) {
if (api) {
@ -119,14 +142,12 @@ export default {
this.httpVisible = true;
}
},
}
}
},
};
</script>
<style scoped>
.create-tip {
color: #8c939d;
}
</style>

View File

@ -1,46 +1,102 @@
<template>
<el-dialog :close-on-click-modal="false" :title="$t('api_test.definition.request.title')" :visible.sync="httpVisible"
width="45%"
:destroy-on-close="true" append-to-body>
<el-form :model="httpForm" label-position="right" label-width="80px" size="small" :rules="rule" ref="httpForm"
v-if="!loading">
<el-dialog
:close-on-click-modal="false"
:title="$t('api_test.definition.request.title')"
:visible.sync="httpVisible"
width="45%"
:destroy-on-close="true"
append-to-body
>
<el-form
:model="httpForm"
label-position="right"
label-width="80px"
size="small"
:rules="rule"
ref="httpForm"
v-if="!loading"
>
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="httpForm.name" autocomplete="off" :placeholder="$t('commons.name')"/>
<el-input
v-model="httpForm.name"
autocomplete="off"
:placeholder="$t('commons.name')"
/>
</el-form-item>
<!--HTTP 协议特有参数-->
<el-form-item :label="$t('api_report.request')" prop="path" v-if="currentProtocol==='HTTP'">
<el-input :placeholder="$t('api_test.definition.request.path_info')" v-model="httpForm.path"
class="ms-http-input" size="small">
<el-select v-model="httpForm.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in options" :key="item.id" :label="item.label" :value="item.id"/>
<el-form-item
:label="$t('api_report.request')"
prop="path"
v-if="currentProtocol === 'HTTP'"
>
<el-input
:placeholder="$t('api_test.definition.request.path_info')"
v-model="httpForm.path"
class="ms-http-input"
size="small"
>
<el-select
v-model="httpForm.method"
slot="prepend"
style="width: 100px"
size="small"
>
<el-option
v-for="item in options"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</el-input>
</el-form-item>
<el-form-item :label="$t('api_test.definition.request.responsible')" prop="userId">
<el-select v-model="httpForm.userId"
:placeholder="$t('api_test.definition.request.responsible')" filterable size="small"
style="width: 100%">
<el-form-item
:label="$t('api_test.definition.request.responsible')"
prop="userId"
>
<el-select
v-model="httpForm.userId"
:placeholder="$t('api_test.definition.request.responsible')"
filterable
size="small"
style="width: 100%"
>
<el-option
v-for="item in maintainerOptions"
:key="item.id"
:label="item.id + ' (' + item.name + ')'"
:value="item.id">
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('test_track.module.module')" prop="moduleId">
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="httpForm.moduleId" @getValue="setModule"
:obj="moduleObj" clearable checkStrictly/>
<ms-select-tree
size="small"
:data="moduleOptions"
:defaultKey="httpForm.moduleId"
@getValue="setModule"
:obj="moduleObj"
clearable
checkStrictly
/>
</el-form-item>
<el-form-item :label="$t('commons.description')" prop="description" style="margin-bottom: 29px">
<el-input class="ms-http-textarea" v-model="httpForm.description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 10}"
:rows="2" size="small"/>
<el-form-item
:label="$t('commons.description')"
prop="description"
style="margin-bottom: 29px"
>
<el-input
class="ms-http-textarea"
v-model="httpForm.description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 10 }"
:rows="2"
size="small"
/>
</el-form-item>
<el-form-item class="create-tip">
{{ $t('api_test.definition.create_tip') }}
@ -50,46 +106,51 @@
<template v-slot:footer>
<ms-dialog-footer
@cancel="httpVisible = false"
@confirm="saveApi" v-prevent-re-click>
@confirm="saveApi"
v-prevent-re-click
>
</ms-dialog-footer>
</template>
</el-dialog>
</template>
<script>
import {createApiCase} from "@/api/api-test-case";
import {createDefinition} from "@/api/definition";
import {getApiModules} from "@/api/definition-module";
import {getMaintainer} from "@/api/project";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import {REQ_METHOD} from "@/business/definition/model/JsonData";
import {getCurrentProjectID, getCurrentUser} from "metersphere-frontend/src/utils/token";
import {createComponent, Request} from "@/business/definition/components/jmeter/components";
import {getUUID} from "metersphere-frontend/src/utils";
import MsSelectTree from "metersphere-frontend/src/components/select-tree/SelectTree";
import {buildTree} from "metersphere-frontend/src/model/NodeTree";
import { createApiCase } from '@/api/api-test-case';
import { createDefinition } from '@/api/definition';
import { getApiModules } from '@/api/definition-module';
import { getMaintainer } from '@/api/project';
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
import { REQ_METHOD } from '@/business/definition/model/JsonData';
import {
getCurrentProjectID,
getCurrentUser,
} from 'metersphere-frontend/src/utils/token';
import {
createComponent,
Request,
} from '@/business/definition/components/jmeter/components';
import { getUUID } from 'metersphere-frontend/src/utils';
import MsSelectTree from 'metersphere-frontend/src/components/select-tree/SelectTree';
import { buildTree } from 'metersphere-frontend/src/model/NodeTree';
export default {
name: "MsAddBasisApi",
components: {MsDialogFooter, MsSelectTree},
name: 'MsAddBasisApi',
components: { MsDialogFooter, MsSelectTree },
props: {
currentProtocol: {
type: String,
default: "HTTP"
default: 'HTTP',
},
},
data() {
let validateURL = (rule, value, callback) => {
if (!this.httpForm.path.startsWith("/")) {
if (!this.httpForm.path.startsWith('/')) {
callback(this.$t('api_test.definition.request.path_valid_info'));
}
callback();
};
return {
httpForm: {environmentId: "", moduleId: "default-module"},
httpForm: { environmentId: '', moduleId: 'default-module' },
moduleOptions: [],
httpVisible: false,
currentModule: {},
@ -101,22 +162,38 @@ export default {
},
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 100, message: this.$t('test_track.length_less_than') + '100', trigger: 'blur'}
{
required: true,
message: this.$t('test_track.case.input_name'),
trigger: 'blur',
},
{
max: 100,
message: this.$t('test_track.length_less_than') + '100',
trigger: 'blur',
},
],
path: [
{
required: true,
message: this.$t('api_test.definition.request.path_info'),
trigger: 'blur',
},
{ validator: validateURL, trigger: 'blur' },
],
userId: [
{
required: true,
message: this.$t('test_track.case.input_maintainer'),
trigger: 'change',
},
],
path: [{
required: true,
message: this.$t('api_test.definition.request.path_info'),
trigger: 'blur'
}, {validator: validateURL, trigger: 'blur'}],
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
// moduleId: [{required: true, message: this.$t('test_track.module.module'), trigger: 'change'}],
},
value: REQ_METHOD[0].id,
options: REQ_METHOD,
}
}
,
};
},
methods: {
saveApi() {
this.$refs['httpForm'].validate((valid) => {
@ -125,7 +202,7 @@ export default {
} else {
return false;
}
})
});
},
save(data) {
this.setParameters(data);
@ -141,7 +218,7 @@ export default {
priority: 'P0',
active: true,
uuid: getUUID(),
request: api.request
request: api.request,
};
obj.projectId = getCurrentProjectID();
obj.id = obj.uuid;
@ -155,8 +232,8 @@ export default {
setParameters(data) {
data.projectId = getCurrentProjectID();
data.request.name = data.name;
if (data.protocol === "DUBBO" || data.protocol === "dubbo://") {
data.request.protocol = "dubbo://";
if (data.protocol === 'DUBBO' || data.protocol === 'dubbo://') {
data.request.protocol = 'dubbo://';
}
data.id = data.request.id;
if (!data.method) {
@ -164,13 +241,21 @@ export default {
}
data.request.path = this.httpForm.path;
if (data.request.hashTree && data.request.hashTree.length > 0) {
let arrays = ["Extract", "JSR223PreProcessor", "JDBCPreProcessor", "ConstantTimer", "JSR223PostProcessor", "JDBCPostProcessor", "Assertions"];
let arrays = [
'Extract',
'JSR223PreProcessor',
'JDBCPreProcessor',
'ConstantTimer',
'JSR223PostProcessor',
'JDBCPostProcessor',
'Assertions',
];
let hashTree = [];
data.request.hashTree.forEach(item => {
data.request.hashTree.forEach((item) => {
if (arrays.indexOf(item.type) !== -1) {
hashTree.push(item);
}
})
});
data.request.hashTree = hashTree;
}
},
@ -180,9 +265,9 @@ export default {
let request = data.request;
if (request.body) {
if (request.body.kvs) {
request.body.kvs.forEach(param => {
request.body.kvs.forEach((param) => {
if (param.files) {
param.files.forEach(item => {
param.files.forEach((item) => {
if (item.file) {
let fileId = getUUID().substring(0, 8);
item.name = item.file.name;
@ -195,9 +280,9 @@ export default {
});
}
if (request.body.binary) {
request.body.binary.forEach(param => {
request.body.binary.forEach((param) => {
if (param.files) {
param.files.forEach(item => {
param.files.forEach((item) => {
if (item.file) {
let fileId = getUUID().substring(0, 8);
item.name = item.file.name;
@ -239,61 +324,65 @@ export default {
this.httpForm.request.path = this.httpForm.path;
}
if (this.currentModule != null) {
this.httpForm.modulePath = this.currentModule.method != undefined ? this.currentModule.method : null;
this.httpForm.modulePath =
this.currentModule.method != undefined
? this.currentModule.method
: null;
this.httpForm.moduleId = this.currentModule.id;
}
},
initHTTP() {
let request = createComponent("HTTPSamplerProxy");
let request = createComponent('HTTPSamplerProxy');
request.path = this.httpForm.path;
this.httpForm.request = request;
},
initSQL() {
this.httpForm.method = Request.TYPES.SQL;
this.httpForm.request = createComponent("JDBCSampler");
this.httpForm.request = createComponent('JDBCSampler');
},
initTCP() {
this.httpForm.method = Request.TYPES.TCP;
this.httpForm.request = createComponent("TCPSampler");
this.httpForm.request = createComponent('TCPSampler');
},
initDUBBO() {
this.httpForm.method = "dubbo://";
this.httpForm.request = createComponent("DubboSampler");
this.httpForm.method = 'dubbo://';
this.httpForm.request = createComponent('DubboSampler');
},
getMaintainerOptions() {
getMaintainer().then(response => {
getMaintainer().then((response) => {
this.maintainerOptions = response.data;
});
},
list(data) {
if (data.protocol === "dubbo://") {
data.protocol = "DUBBO";
if (data.protocol === 'dubbo://') {
data.protocol = 'DUBBO';
}
this.result = getApiModules(getCurrentProjectID(), data.protocol).then(response => {
if (response.data != undefined && response.data != null) {
this.moduleOptions = response.data;
this.moduleOptions.forEach(node => {
buildTree(node, {path: ''});
});
this.result = getApiModules(getCurrentProjectID(), data.protocol).then(
(response) => {
if (response.data != undefined && response.data != null) {
this.moduleOptions = response.data;
this.moduleOptions.forEach((node) => {
buildTree(node, { path: '' });
});
}
}
});
);
},
setModule(id, data) {
this.httpForm.moduleId = id;
this.httpForm.modulePath = data.path;
},
reload() {
this.loading = true
this.loading = true;
this.$nextTick(() => {
this.loading = false
})
this.loading = false;
});
},
open(api) {
if (api) {
let data = JSON.parse(JSON.stringify(api));
if (data.protocol === "dubbo://") {
data.protocol = "DUBBO";
if (data.protocol === 'dubbo://') {
data.protocol = 'DUBBO';
}
data.id = getUUID();
data.path = this.httpForm.path;
@ -305,21 +394,19 @@ export default {
method: api.method,
userId: getCurrentUser().id,
request: data,
moduleId: "default-module"
moduleId: 'default-module',
};
this.getMaintainerOptions();
this.list(data);
this.httpVisible = true;
}
},
}
}
},
};
</script>
<style scoped>
.create-tip {
color: #8c939d;
}
</style>

View File

@ -3,7 +3,8 @@
:is-across-space="isAcrossSpace"
@setProject="setProject"
:dialog-title="$t('api_test.definition.api_import')"
ref="baseRelevance">
ref="baseRelevance"
>
<template v-slot:aside>
<ms-api-module
@nodeSelectEvent="nodeChange"
@ -13,7 +14,8 @@
:is-relevance="true"
:is-read-only="true"
:select-project-id="projectId"
ref="nodeTree"/>
ref="nodeTree"
/>
</template>
<scenario-relevance-api-list
@ -26,10 +28,15 @@
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange"
@selectCountChange="setSelectCounts"
ref="apiList">
ref="apiList"
>
<template v-slot:version>
<mx-version-select v-xpack :project-id="projectId" :default-version="currentVersion"
@changeVersion="currentVersionChange"/>
<mx-version-select
v-xpack
:project-id="projectId"
:default-version="currentVersion"
@changeVersion="currentVersionChange"
/>
</template>
</scenario-relevance-api-list>
@ -43,23 +50,43 @@
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange"
@selectCountChange="setSelectCounts"
ref="apiCaseList">
ref="apiCaseList"
>
<template v-slot:version>
<mx-version-select v-xpack :project-id="projectId" :default-version="currentVersion"
@changeVersion="currentVersionChange"/>
<mx-version-select
v-xpack
:project-id="projectId"
:default-version="currentVersion"
@changeVersion="currentVersionChange"
/>
</template>
</scenario-relevance-case-list>
<template v-slot:headerBtn>
<!-- 显示数量 -->
<table-select-count-bar :count="selectCounts" style="float: left; margin: 5px;"/>
<table-select-count-bar
:count="selectCounts"
style="float: left; margin: 5px"
/>
<el-button size="mini" icon="el-icon-refresh" @click="refresh"/>
<el-button type="primary" @click="copy" :loading="buttonIsWorking" @keydown.enter.native.prevent size="mini">
<el-button size="mini" icon="el-icon-refresh" @click="refresh" />
<el-button
type="primary"
@click="copy"
:loading="buttonIsWorking"
@keydown.enter.native.prevent
size="mini"
>
{{ $t('commons.copy') }}
</el-button>
<el-button v-if="!isApiListEnable" type="primary" :loading="buttonIsWorking" @click="reference" size="mini"
@keydown.enter.native.prevent>
<el-button
v-if="!isApiListEnable"
type="primary"
:loading="buttonIsWorking"
@click="reference"
size="mini"
@keydown.enter.native.prevent
>
{{ $t('api_test.scenario.reference') }}
</el-button>
</template>
@ -67,37 +94,42 @@
</template>
<script>
import {getApiCaseWithBLOBs} from "@/api/api-test-case";
import {apiListBatch} from "@/api/definition";
import {getProjectVersions} from "@/api/xpack";
import ScenarioRelevanceCaseList from "./RelevanceCaseList";
import MsApiModule from "../../../definition/components/module/ApiModule";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsAsideContainer from "metersphere-frontend/src/components/MsAsideContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import ScenarioRelevanceApiList from "./RelevanceApiList";
import RelevanceDialog from "@/business/commons/RelevanceDialog";
import TestCaseRelevanceBase from "@/business/commons/TestCaseRelevanceBase";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import TableSelectCountBar from "@/business/automation/scenario/api/TableSelectCountBar";
import { getApiCaseWithBLOBs } from '@/api/api-test-case';
import { apiListBatch } from '@/api/definition';
import { getProjectVersions } from '@/api/xpack';
import ScenarioRelevanceCaseList from './RelevanceCaseList';
import MsApiModule from '../../../definition/components/module/ApiModule';
import MsContainer from 'metersphere-frontend/src/components/MsContainer';
import MsAsideContainer from 'metersphere-frontend/src/components/MsAsideContainer';
import MsMainContainer from 'metersphere-frontend/src/components/MsMainContainer';
import ScenarioRelevanceApiList from './RelevanceApiList';
import RelevanceDialog from '@/business/commons/RelevanceDialog';
import TestCaseRelevanceBase from '@/business/commons/TestCaseRelevanceBase';
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
import TableSelectCountBar from '@/business/automation/scenario/api/TableSelectCountBar';
export default {
name: "ApiRelevance",
name: 'ApiRelevance',
components: {
MxVersionSelect: () => import("metersphere-frontend/src/components/version/MxVersionSelect"),
MxVersionSelect: () =>
import('metersphere-frontend/src/components/version/MxVersionSelect'),
TableSelectCountBar,
TestCaseRelevanceBase,
RelevanceDialog,
ScenarioRelevanceApiList,
MsMainContainer, MsAsideContainer, MsContainer, MsApiModule, ScenarioRelevanceCaseList
MsMainContainer,
MsAsideContainer,
MsContainer,
MsApiModule,
ScenarioRelevanceCaseList,
},
props: {
isAcrossSpace: {
type: Boolean,
default() {
return false;
}
}
},
},
},
data() {
return {
@ -108,7 +140,7 @@ export default {
selectNodeIds: [],
moduleOptions: {},
isApiListEnable: true,
projectId: "",
projectId: '',
versionFilters: [],
currentVersion: null,
selectCounts: null,
@ -119,7 +151,7 @@ export default {
this.refresh();
this.$refs.nodeTree.list(this.projectId);
this.getVersionOptions();
}
},
},
methods: {
changeButtonLoadingType() {
@ -137,37 +169,42 @@ export default {
save(reference) {
if (this.isApiListEnable) {
let apis = this.$refs.apiList.selectRows;
apis.forEach(api => {
apis.forEach((api) => {
api.projectId = this.projectId;
});
let params = this.$refs.apiList.getConditions();
this.result = apiListBatch(params).then((response) => {
let apis = response.data;
if (apis.length === 0) {
this.$warning('请选择接口');
this.result = apiListBatch(params).then(
(response) => {
let apis = response.data;
if (apis.length === 0) {
this.$warning('请选择接口');
this.buttonIsWorking = false;
} else {
this.$emit('save', apis, 'API', reference);
this.$refs.baseRelevance.close();
}
},
(error) => {
this.buttonIsWorking = false;
} else {
this.$emit('save', apis, 'API', reference);
this.$refs.baseRelevance.close();
}
}, (error) => {
this.buttonIsWorking = false;
});
);
} else {
let params = this.$refs.apiCaseList.getConditions();
this.result = getApiCaseWithBLOBs(params).then((response) => {
let apiCases = response.data;
if (apiCases.length === 0) {
this.$warning('请选择案例');
this.result = getApiCaseWithBLOBs(params).then(
(response) => {
let apiCases = response.data;
if (apiCases.length === 0) {
this.$warning('请选择案例');
this.buttonIsWorking = false;
} else {
this.$emit('save', apiCases, 'CASE', reference);
this.$refs.baseRelevance.close();
}
},
(error) => {
this.buttonIsWorking = false;
} else {
this.$emit('save', apiCases, 'CASE', reference);
this.$refs.baseRelevance.close();
}
}, (error) => {
this.buttonIsWorking = false;
});
);
}
},
close() {
@ -211,14 +248,16 @@ export default {
if (!this.projectId) {
return;
}
getProjectVersions(this.projectId).then(response => {
getProjectVersions(this.projectId).then((response) => {
if (currentVersion) {
this.versionFilters = response.data.filter(u => u.id === currentVersion).map(u => {
return {text: u.name, value: u.id};
});
this.versionFilters = response.data
.filter((u) => u.id === currentVersion)
.map((u) => {
return { text: u.name, value: u.id };
});
} else {
this.versionFilters = response.data.map(u => {
return {text: u.name, value: u.id};
this.versionFilters = response.data.map((u) => {
return { text: u.name, value: u.id };
});
}
});
@ -227,7 +266,7 @@ export default {
setSelectCounts(data) {
this.selectCounts = data;
},
}
},
};
</script>

View File

@ -2,8 +2,8 @@
<div v-loading="result">
<api-list-container
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange">
@isApiListEnableChange="isApiListEnableChange"
>
<template>
<slot name="version"></slot>
</template>
@ -22,34 +22,35 @@
@setSelectRow="setSelectRow"
@selectCountChange="selectCountChange"
@refreshTable="initTable"
ref="apitable">
ref="apitable"
>
<template v-slot:header>
<ms-environment-select :project-id="projectId" v-if="isTestPlan || isScript" :is-read-only="isReadOnly"
@setEnvironment="setEnvironment" ref="msEnvironmentSelect"/>
<ms-environment-select
:project-id="projectId"
v-if="isTestPlan || isScript"
:is-read-only="isReadOnly"
@setEnvironment="setEnvironment"
ref="msEnvironmentSelect"
/>
</template>
</api-table-list>
</api-list-container>
</div>
</template>
<script>
import {getDefinitionPage, getRelevanceDefinitionPage} from "@/api/definition";
import ApiListContainer from "@/business/definition/components/list/ApiListContainer";
import MsEnvironmentSelect from "@/business/definition/components/case/MsEnvironmentSelect";
import {buildBatchParam} from "metersphere-frontend/src/utils/tableUtils";
import {
TEST_PLAN_RELEVANCE_API_DEFINITION_CONFIGS,
} from "metersphere-frontend/src/components/search/search-components";
import ApiTableList from "@/business/definition/components/complete/ApiTableList";
getDefinitionPage,
getRelevanceDefinitionPage,
} from '@/api/definition';
import ApiListContainer from '@/business/definition/components/list/ApiListContainer';
import MsEnvironmentSelect from '@/business/definition/components/case/MsEnvironmentSelect';
import { buildBatchParam } from 'metersphere-frontend/src/utils/tableUtils';
import { TEST_PLAN_RELEVANCE_API_DEFINITION_CONFIGS } from 'metersphere-frontend/src/components/search/search-components';
import ApiTableList from '@/business/definition/components/complete/ApiTableList';
export default {
name: "RelevanceApiList",
name: 'RelevanceApiList',
components: {
ApiTableList,
MsEnvironmentSelect,
@ -58,12 +59,12 @@ export default {
data() {
return {
condition: {
components: TEST_PLAN_RELEVANCE_API_DEFINITION_CONFIGS
components: TEST_PLAN_RELEVANCE_API_DEFINITION_CONFIGS,
},
result: false,
screenHeight: 'calc(100vh - 400px)',//,
screenHeight: 'calc(100vh - 400px)', //,
tableData: [],
environmentId: "",
environmentId: '',
total: 0,
selectRows: new Set(),
};
@ -81,7 +82,7 @@ export default {
type: Boolean,
default() {
return false;
}
},
},
isApiListEnable: {
type: Boolean,
@ -89,7 +90,7 @@ export default {
},
isReadOnly: {
type: Boolean,
default: false
default: false,
},
isCaseRelevance: {
type: Boolean,
@ -100,8 +101,8 @@ export default {
isTestPlan: Boolean,
versionEnable: {
type: Boolean,
default: false
}
default: false,
},
},
created() {
this.condition.versionId = this.currentVersion;
@ -116,7 +117,7 @@ export default {
},
projectId() {
this.condition = {
components: TEST_PLAN_RELEVANCE_API_DEFINITION_CONFIGS
components: TEST_PLAN_RELEVANCE_API_DEFINITION_CONFIGS,
};
this.selectNodeIds.length = 0;
this.initTable();
@ -124,7 +125,7 @@ export default {
currentVersion() {
this.condition.versionId = this.currentVersion;
this.initTable();
}
},
},
methods: {
setSelectRow(setSelectRow) {
@ -139,7 +140,7 @@ export default {
initTable(projectId) {
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
this.condition.filters = {status: ["Trash"]};
this.condition.filters = { status: ['Trash'] };
this.condition.moduleIds = [];
}
if (projectId != null && typeof projectId === 'string') {
@ -151,23 +152,33 @@ export default {
if (this.currentProtocol != null) {
this.condition.protocol = this.currentProtocol;
} else {
this.condition.protocol = "HTTP";
this.condition.protocol = 'HTTP';
}
if (this.condition.filters) {
this.condition.filters.status = ["Prepare", "Underway", "Completed"];
this.condition.filters.status = ['Prepare', 'Underway', 'Completed'];
} else {
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
this.condition.filters = {
status: ['Prepare', 'Underway', 'Completed'],
};
}
if (this.isTestPlan) {
this.condition.planId = this.planId;
this.$nextTick(() => {
this.result = getRelevanceDefinitionPage(this.$refs.apitable.currentPage, this.$refs.apitable.pageSize, this.condition).then(response => {
this.result = getRelevanceDefinitionPage(
this.$refs.apitable.currentPage,
this.$refs.apitable.pageSize,
this.condition
).then((response) => {
this.setData(response);
});
});
} else {
this.$nextTick(() => {
this.result = getDefinitionPage(this.$refs.apitable.currentPage, this.$refs.apitable.pageSize, this.condition).then(response => {
this.result = getDefinitionPage(
this.$refs.apitable.currentPage,
this.$refs.apitable.pageSize,
this.condition
).then((response) => {
this.setData(response);
});
});
@ -176,7 +187,7 @@ export default {
setData(response) {
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
this.tableData.forEach(item => {
this.tableData.forEach((item) => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
@ -188,8 +199,8 @@ export default {
getConditions() {
let sampleSelectRows = this.selectRows;
let param = buildBatchParam(this, undefined, this.projectId);
param.ids = Array.from(sampleSelectRows).map(row => row.id);
let tableDataIds = Array.from(this.tableData).map(row => row.id);
param.ids = Array.from(sampleSelectRows).map((row) => row.id);
let tableDataIds = Array.from(this.tableData).map((row) => row.id);
param.ids.sort((a, b) => {
return tableDataIds.indexOf(a) - tableDataIds.indexOf(b);
});
@ -201,16 +212,14 @@ export default {
}
},
clearEnvAndSelect() {
this.environmentId = "";
this.environmentId = '';
if (this.$refs.msEnvironmentSelect) {
this.$refs.msEnvironmentSelect.environmentId = "";
this.$refs.msEnvironmentSelect.environmentId = '';
}
this.clear();
}
},
},
}
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -2,44 +2,51 @@
<div v-loading="result">
<api-list-container
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange">
@isApiListEnableChange="isApiListEnableChange"
>
<template>
<slot name="version"></slot>
</template>
<ms-environment-select :project-id="projectId" v-if="isTestPlan || isScript" :is-read-only="isReadOnly"
@setEnvironment="setEnvironment" ref="msEnvironmentSelect"/>
<ms-search
:condition.sync="condition"
@search="initTable">
</ms-search>
<ms-table :data="tableData" :select-node-ids="selectNodeIds" :condition="condition" :page-size="pageSize"
:total="total" enableSelection
:screenHeight="screenHeight"
@refresh="initTable"
@selectCountChange="selectCountChange"
operator-width="170px"
ref="table"
<ms-environment-select
:project-id="projectId"
v-if="isTestPlan || isScript"
:is-read-only="isReadOnly"
@setEnvironment="setEnvironment"
ref="msEnvironmentSelect"
/>
<ms-search :condition.sync="condition" @search="initTable"> </ms-search>
<ms-table
:data="tableData"
:select-node-ids="selectNodeIds"
:condition="condition"
:page-size="pageSize"
:total="total"
enableSelection
:screenHeight="screenHeight"
@refresh="initTable"
@selectCountChange="selectCountChange"
operator-width="170px"
ref="table"
>
<ms-table-column
prop="num"
label="ID"
width="80px"
sortable=true>
<ms-table-column prop="num" label="ID" width="80px" sortable="true">
</ms-table-column>
<ms-table-column prop="name" width="160px" :label="$t('test_track.case.name')"/>
<ms-table-column
prop="name"
width="160px"
:label="$t('test_track.case.name')"
/>
<ms-table-column
prop="priority"
:filters="priorityFilters"
column-key="priority"
width="120px"
:label="$t('test_track.case.priority')">
:label="$t('test_track.case.priority')"
>
<template v-slot:default="scope">
<priority-table-item :value="scope.row.priority"/>
<priority-table-item :value="scope.row.priority" />
</template>
</ms-table-column>
@ -47,12 +54,19 @@
sortable="custom"
prop="path"
width="180px"
:label="'API'+ $t('api_test.definition.api_path')"/>
:label="'API' + $t('api_test.definition.api_path')"
/>
<ms-table-column prop="tags" width="120px" :label="$t('commons.tag')">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" style="margin-left: 0px; margin-right: 2px"/>
<ms-tag
v-for="(itemName, index) in scope.row.tags"
:key="index"
type="success"
effect="plain"
:content="itemName"
style="margin-left: 0px; margin-right: 2px"
/>
</template>
</ms-table-column>
@ -61,21 +75,21 @@
:label="$t('project.version.name')"
:filters="versionFilters"
min-width="100px"
prop="versionId">
prop="versionId"
>
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</ms-table-column>
<ms-table-column
prop="createUser"
:label="$t('commons.create_user')"/>
<ms-table-column prop="createUser" :label="$t('commons.create_user')" />
<ms-table-column
sortable="createTime"
width="160px"
:label="$t('commons.create_time')"
prop="createTime">
prop="createTime"
>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
@ -85,48 +99,55 @@
sortable="updateTime"
width="160px"
:label="$t('api_test.definition.api_last_time')"
prop="updateTime">
prop="updateTime"
>
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</ms-table-column>
</ms-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<ms-table-pagination
:change="initTable"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
/>
</api-list-container>
</div>
</template>
<script>
import {apiTestCasePage} from "@/api/api-test-case";
import {getDefinitionById} from "@/api/definition";
import {versionEnableByProjectId} from "@/api/xpack";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import MsTableOperator from "metersphere-frontend/src/components/MsTableOperator";
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import MsTag from "metersphere-frontend/src/components/MsTag";
import MsBottomContainer from "@/business/definition/components/BottomContainer";
import ShowMoreBtn from "@/business/commons/ShowMoreBtn";
import MsBatchEdit from "@/business/definition/components/basis/BatchEdit";
import {API_METHOD_COLOUR, CASE_PRIORITY} from "@/business/definition/model/JsonData";
import ApiListContainer from "@/business/definition/components/list/ApiListContainer";
import PriorityTableItem from "@/business/commons/PriorityTableItem";
import MsEnvironmentSelect from "@/business/definition/components/case/MsEnvironmentSelect";
import {_filter, _sort, buildBatchParam} from "metersphere-frontend/src/utils/tableUtils";
import MsTableAdvSearchBar from "metersphere-frontend/src/components/search/MsTableAdvSearchBar";
import { apiTestCasePage } from '@/api/api-test-case';
import { getDefinitionById } from '@/api/definition';
import { versionEnableByProjectId } from '@/api/xpack';
import MsTable from 'metersphere-frontend/src/components/table/MsTable';
import MsTableColumn from 'metersphere-frontend/src/components/table/MsTableColumn';
import MsTableOperator from 'metersphere-frontend/src/components/MsTableOperator';
import MsTableOperatorButton from 'metersphere-frontend/src/components/MsTableOperatorButton';
import MsTablePagination from 'metersphere-frontend/src/components/pagination/TablePagination';
import MsTag from 'metersphere-frontend/src/components/MsTag';
import MsBottomContainer from '@/business/definition/components/BottomContainer';
import ShowMoreBtn from '@/business/commons/ShowMoreBtn';
import MsBatchEdit from '@/business/definition/components/basis/BatchEdit';
import {
TEST_PLAN_RELEVANCE_API_CASE_CONFIGS
} from "metersphere-frontend/src/components/search/search-components";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import MsSearch from "metersphere-frontend/src/components/search/MsSearch";
API_METHOD_COLOUR,
CASE_PRIORITY,
} from '@/business/definition/model/JsonData';
import ApiListContainer from '@/business/definition/components/list/ApiListContainer';
import PriorityTableItem from '@/business/commons/PriorityTableItem';
import MsEnvironmentSelect from '@/business/definition/components/case/MsEnvironmentSelect';
import {
_filter,
_sort,
buildBatchParam,
} from 'metersphere-frontend/src/utils/tableUtils';
import MsTableAdvSearchBar from 'metersphere-frontend/src/components/search/MsTableAdvSearchBar';
import { TEST_PLAN_RELEVANCE_API_CASE_CONFIGS } from 'metersphere-frontend/src/components/search/search-components';
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
import MsSearch from 'metersphere-frontend/src/components/search/MsSearch';
export default {
name: "RelevanceCaseList",
name: 'RelevanceCaseList',
components: {
MsEnvironmentSelect,
PriorityTableItem,
@ -141,35 +162,33 @@ export default {
MsTable,
MsTableColumn,
MsSearch,
MsTableAdvSearchBar
MsTableAdvSearchBar,
},
data() {
return {
condition: {
components: TEST_PLAN_RELEVANCE_API_CASE_CONFIGS
components: TEST_PLAN_RELEVANCE_API_CASE_CONFIGS,
},
selectCase: {},
result: false,
moduleId: "",
typeArr: [
{id: 'priority', name: this.$t('test_track.case.priority')},
],
moduleId: '',
typeArr: [{ id: 'priority', name: this.$t('test_track.case.priority') }],
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
{ text: 'P0', value: 'P0' },
{ text: 'P1', value: 'P1' },
{ text: 'P2', value: 'P2' },
{ text: 'P3', value: 'P3' },
],
valueArr: {
priority: CASE_PRIORITY,
},
methodColorMap: new Map(API_METHOD_COLOUR),
screenHeight: 'calc(100vh - 400px)',//
screenHeight: 'calc(100vh - 400px)', //
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
environmentId: "",
environmentId: '',
versionEnable: false,
};
},
@ -190,11 +209,11 @@ export default {
type: Boolean,
default() {
return false;
}
},
},
isReadOnly: {
type: Boolean,
default: false
default: false,
},
isCaseRelevance: {
type: Boolean,
@ -218,7 +237,7 @@ export default {
},
projectId() {
this.condition = {
components: TEST_PLAN_RELEVANCE_API_CASE_CONFIGS
components: TEST_PLAN_RELEVANCE_API_CASE_CONFIGS,
};
this.selectNodeIds.length = 0;
this.initTable();
@ -227,7 +246,7 @@ export default {
currentVersion() {
this.condition.versionId = this.currentVersion;
this.initTable();
}
},
},
computed: {
selectRows() {
@ -236,7 +255,7 @@ export default {
} else {
return new Set();
}
}
},
},
methods: {
isApiListEnableChange(data) {
@ -246,7 +265,7 @@ export default {
this.$emit('selectCountChange', data);
},
initTable(projectId) {
this.condition.status = "";
this.condition.status = '';
this.condition.moduleIds = this.selectNodeIds;
if (projectId != null && typeof projectId === 'string') {
this.condition.projectId = projectId;
@ -257,14 +276,18 @@ export default {
this.condition.protocol = this.currentProtocol;
}
this.condition.ids = [];
this.result = apiTestCasePage(this.currentPage, this.pageSize, this.condition).then(response => {
this.result = apiTestCasePage(
this.currentPage,
this.pageSize,
this.condition
).then((response) => {
this.setData(response);
});
},
setData(response) {
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
this.tableData.forEach(item => {
this.tableData.forEach((item) => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
@ -276,9 +299,9 @@ export default {
}
},
clearEnvAndSelect() {
this.environmentId = "";
this.environmentId = '';
if (this.$refs.msEnvironmentSelect) {
this.$refs.msEnvironmentSelect.environmentId = "";
this.$refs.msEnvironmentSelect.environmentId = '';
}
this.clear();
},
@ -299,14 +322,19 @@ export default {
this.initTable();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
return path + '/' + this.currentPage + '/' + this.pageSize;
},
handleTestCase(testCase) {
getDefinitionById(testCase.apiDefinitionId).then((response) => {
let api = response.data;
let selectApi = api;
let request = {};
if (Object.prototype.toString.call(api.request).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
if (
Object.prototype.toString
.call(api.request)
.match(/\[object (\w+)\]/)[1]
.toLowerCase() === 'object'
) {
request = api.request;
} else {
request = JSON.parse(api.request);
@ -337,8 +365,8 @@ export default {
} else {
param = batchParam;
}
param.ids = Array.from(sampleSelectRows).map(row => row.id);
let tableDataIds = Array.from(this.tableData).map(row => row.id);
param.ids = Array.from(sampleSelectRows).map((row) => row.id);
let tableDataIds = Array.from(this.tableData).map((row) => row.id);
param.ids.sort((a, b) => {
return tableDataIds.indexOf(a) - tableDataIds.indexOf(b);
});
@ -349,11 +377,11 @@ export default {
return;
}
if (hasLicense()) {
versionEnableByProjectId(this.projectId).then(response => {
versionEnableByProjectId(this.projectId).then((response) => {
this.versionEnable = response.data;
});
}
}
},
},
};
</script>
@ -366,7 +394,7 @@ export default {
.request-method {
padding: 0 5px;
color: #1E90FF;
color: #1e90ff;
}
.api-el-tag {
@ -383,5 +411,4 @@ export default {
margin-top: 5px;
margin-right: 10px;
}
</style>

View File

@ -3,103 +3,163 @@
<ms-search
:base-search-tip="$t('api_test.definition.request.select_case')"
:condition.sync="condition"
@search="search">
@search="search"
>
</ms-search>
<mx-version-select v-xpack :project-id="projectId" @changeVersion="changeVersion"
style="float: left"
class="search-input"/>
<mx-version-select
v-xpack
:project-id="projectId"
@changeVersion="changeVersion"
style="float: left"
class="search-input"
/>
<ms-table ref="scenarioTable"
v-loading="result"
:data="tableData"
:condition="condition"
:page-size="pageSize"
:total="total"
:remember-order="true"
row-key="id"
:row-order-group-id="projectId"
@refresh="search"
:disable-header-config="true"
@selectCountChange="selectCountChange">
<el-table-column v-if="!customNum" prop="num" label="ID"
show-overflow-tooltip>
<ms-table
ref="scenarioTable"
v-loading="result"
:data="tableData"
:condition="condition"
:page-size="pageSize"
:total="total"
:remember-order="true"
row-key="id"
:row-order-group-id="projectId"
@refresh="search"
:disable-header-config="true"
@selectCountChange="selectCountChange"
>
<el-table-column
v-if="!customNum"
prop="num"
label="ID"
show-overflow-tooltip
>
</el-table-column>
<el-table-column v-if="customNum" prop="customNum" label="ID"
show-overflow-tooltip>
<el-table-column
v-if="customNum"
prop="customNum"
label="ID"
show-overflow-tooltip
>
</el-table-column>
<el-table-column prop="name" :label="$t('api_test.automation.scenario_name')"
show-overflow-tooltip/>
<el-table-column
prop="name"
:label="$t('api_test.automation.scenario_name')"
show-overflow-tooltip
/>
<el-table-column
v-if="versionEnable"
column-key="version_id"
:filters="versionFilters"
:label="$t('commons.version')"
min-width="120px">
min-width="120px"
>
<template v-slot:default="scope">
<span>{{ scope.row.versionName }}</span>
</template>
</el-table-column>
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
show-overflow-tooltip>
<el-table-column
prop="level"
:label="$t('api_test.automation.case_level')"
show-overflow-tooltip
>
<template v-slot:default="scope">
<priority-table-item :value="scope.row.level" ref="level"/>
</template>
</el-table-column>
<el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" min-width="120">
<template v-slot:default="scope">
<ms-tag v-for="itemName in scope.row.tags" :key="itemName" type="success" effect="plain" :content="itemName"
style="margin-left: 0px; margin-right: 2px"/>
<priority-table-item :value="scope.row.level" ref="level" />
</template>
</el-table-column>
<el-table-column prop="userId" :label="$t('api_test.automation.creator')" show-overflow-tooltip/>
<el-table-column prop="updateTime" :label="$t('api_test.automation.update_time')" width="180">
<el-table-column
prop="tagNames"
:label="$t('api_test.automation.tag')"
min-width="120"
>
<template v-slot:default="scope">
<ms-tag
v-for="itemName in scope.row.tags"
:key="itemName"
type="success"
effect="plain"
:content="itemName"
style="margin-left: 0px; margin-right: 2px"
/>
</template>
</el-table-column>
<el-table-column
prop="userId"
:label="$t('api_test.automation.creator')"
show-overflow-tooltip
/>
<el-table-column
prop="updateTime"
:label="$t('api_test.automation.update_time')"
width="180"
>
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column prop="stepTotal" :label="$t('api_test.automation.step')" show-overflow-tooltip/>
<el-table-column prop="lastResult" :label="$t('api_test.automation.last_result')">
<template v-slot:default="{row}">
<el-link type="success" @click="showReport(row)" v-if="row.lastResult === 'SUCCESS'">
<el-table-column
prop="stepTotal"
:label="$t('api_test.automation.step')"
show-overflow-tooltip
/>
<el-table-column
prop="lastResult"
:label="$t('api_test.automation.last_result')"
>
<template v-slot:default="{ row }">
<el-link
type="success"
@click="showReport(row)"
v-if="row.lastResult === 'SUCCESS'"
>
{{ $t('api_test.automation.success') }}
</el-link>
<el-link type="danger" @click="showReport(row)" v-if="row.lastResult === 'ERROR'">
<el-link
type="danger"
@click="showReport(row)"
v-if="row.lastResult === 'ERROR'"
>
{{ $t('api_test.automation.fail') }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="passRate" :label="$t('api_test.automation.passing_rate')"
show-overflow-tooltip/>
<el-table-column
prop="passRate"
:label="$t('api_test.automation.passing_rate')"
show-overflow-tooltip
/>
</ms-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<ms-table-pagination
:change="search"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
/>
</div>
</template>
<script>
import {getProjectVersions} from "@/api/xpack";
import {getScenarioList} from "@/api/scenario";
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import ShowMoreBtn from "@/business/commons/ShowMoreBtn";
import MsApiReportDetail from "@/business/automation/report/ApiReportDetail";
import MsTableMoreBtn from "@/business/automation/scenario/TableMoreBtn";
import PriorityTableItem from "@/business/commons/PriorityTableItem";
import MsTableAdvSearchBar from "metersphere-frontend/src/components/search/MsTableAdvSearchBar";
import {API_SCENARIO_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTag from "metersphere-frontend/src/components/MsTag";
import MsSearch from "metersphere-frontend/src/components/search/MsSearch";
import {getOwnerProjects, getProjectConfig} from "@/api/project";
import { getProjectVersions } from '@/api/xpack';
import { getScenarioList } from '@/api/scenario';
import MsTableHeader from 'metersphere-frontend/src/components/MsTableHeader';
import MsTablePagination from 'metersphere-frontend/src/components/pagination/TablePagination';
import ShowMoreBtn from '@/business/commons/ShowMoreBtn';
import MsApiReportDetail from '@/business/automation/report/ApiReportDetail';
import MsTableMoreBtn from '@/business/automation/scenario/TableMoreBtn';
import PriorityTableItem from '@/business/commons/PriorityTableItem';
import MsTableAdvSearchBar from 'metersphere-frontend/src/components/search/MsTableAdvSearchBar';
import { API_SCENARIO_CONFIGS } from 'metersphere-frontend/src/components/search/search-components';
import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
import MsTable from 'metersphere-frontend/src/components/table/MsTable';
import MsTag from 'metersphere-frontend/src/components/MsTag';
import MsSearch from 'metersphere-frontend/src/components/search/MsSearch';
import { getOwnerProjects, getProjectConfig } from '@/api/project';
export default {
name: "RelevanceScenarioList",
name: 'RelevanceScenarioList',
components: {
MsTable,
PriorityTableItem,
@ -111,7 +171,8 @@ export default {
MsApiReportDetail,
MsTableAdvSearchBar,
MsSearch,
MxVersionSelect: () => import("metersphere-frontend/src/components/version/MxVersionSelect"),
MxVersionSelect: () =>
import('metersphere-frontend/src/components/version/MxVersionSelect'),
},
props: {
referenced: {
@ -128,7 +189,7 @@ export default {
result: false,
showConfigButtonWithOutPermission: false,
condition: {
components: API_SCENARIO_CONFIGS
components: API_SCENARIO_CONFIGS,
},
currentScenario: {},
schedule: {},
@ -137,7 +198,7 @@ export default {
currentPage: 1,
pageSize: 10,
total: 0,
reportId: "",
reportId: '',
infoDb: false,
selectRows: new Set(),
projectEnvMap: new Map(),
@ -146,14 +207,14 @@ export default {
map: new Map(),
customNum: false,
environmentType: ENV_TYPE.JSON,
envGroupId: "",
envGroupId: '',
versionFilters: [],
};
},
computed: {
ENV_TYPE() {
return ENV_TYPE;
}
},
},
watch: {
selectNodeIds() {
@ -161,7 +222,7 @@ export default {
},
projectId() {
this.condition = {
components: API_SCENARIO_CONFIGS
components: API_SCENARIO_CONFIGS,
};
this.selectNodeIds.length = 0;
this.search();
@ -176,7 +237,7 @@ export default {
this.selectRows = new Set();
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
this.condition.filters = {status: ["Trash"]};
this.condition.filters = { status: ['Trash'] };
this.condition.moduleIds = [];
}
if (typeof currentProjectId === 'string') {
@ -186,11 +247,15 @@ export default {
}
if (this.condition.projectId) {
this.result = getScenarioList(this.currentPage, this.pageSize, this.condition).then(response => {
this.result = getScenarioList(
this.currentPage,
this.pageSize,
this.condition
).then((response) => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
this.tableData.forEach((item) => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
@ -208,13 +273,13 @@ export default {
this.envGroupId = id;
},
getWsProjects() {
getOwnerProjects().then(res => {
getOwnerProjects().then((res) => {
this.projectList = res.data;
});
},
getProject(projectId) {
if (projectId) {
getProjectConfig(projectId, "/SCENARIO_CUSTOM_NUM").then(result => {
getProjectConfig(projectId, '/SCENARIO_CUSTOM_NUM').then((result) => {
let data = result.data;
if (data) {
this.customNum = data.scenarioCustomNum;
@ -223,7 +288,9 @@ export default {
}
},
getConditions() {
this.condition.tableDataIds = Array.from(this.tableData).map(row => row.id);
this.condition.tableDataIds = Array.from(this.tableData).map(
(row) => row.id
);
return this.condition;
},
checkEnv() {
@ -235,9 +302,9 @@ export default {
},
getVersionOptions() {
if (hasLicense()) {
getProjectVersions(getCurrentProjectID()).then(response => {
this.versionFilters = response.data.map(u => {
return {text: u.name, value: u.id};
getProjectVersions(getCurrentProjectID()).then((response) => {
this.versionFilters = response.data.map((u) => {
return { text: u.name, value: u.id };
});
});
}
@ -248,9 +315,9 @@ export default {
},
selectCountChange(data) {
this.selectRows = this.$refs.scenarioTable.selectRows;
this.$emit("selectCountChange", data);
}
}
this.$emit('selectCountChange', data);
},
},
};
</script>

View File

@ -4,17 +4,19 @@
:dialog-title="$t('api_test.automation.scenario_import')"
@setProject="setProject"
@refreshNode="refresh"
ref="baseRelevance">
ref="baseRelevance"
>
<template v-slot:aside>
<ms-api-scenario-module
style="margin-top: 5px;"
style="margin-top: 5px"
@nodeSelectEvent="nodeChange"
@refreshTable="refresh"
@setModuleOptions="setModuleOptions"
@enableTrash="false"
:is-read-only="true"
:select-project-id="projectId"
ref="nodeTree"/>
ref="nodeTree"
/>
</template>
<relevance-scenario-list
@ -24,17 +26,33 @@
:referenced="true"
:trash-enable="false"
@selectCountChange="setSelectCounts"
ref="apiScenarioList">
ref="apiScenarioList"
>
</relevance-scenario-list>
<template v-slot:headerBtn>
<table-select-count-bar :count="selectCounts" style="float: left; margin: 5px;"/>
<table-select-count-bar
:count="selectCounts"
style="float: left; margin: 5px"
/>
<el-button size="mini" icon="el-icon-refresh" @click="refresh"/>
<el-button type="primary" @click="copy" :loading="buttonIsWorking" @keydown.enter.native.prevent size="mini">
<el-button size="mini" icon="el-icon-refresh" @click="refresh" />
<el-button
type="primary"
@click="copy"
:loading="buttonIsWorking"
@keydown.enter.native.prevent
size="mini"
>
{{ $t('commons.copy') }}
</el-button>
<el-button type="primary" @click="reference" :loading="buttonIsWorking" @keydown.enter.native.prevent size="mini">
<el-button
type="primary"
@click="reference"
:loading="buttonIsWorking"
@keydown.enter.native.prevent
size="mini"
>
{{ $t('api_test.scenario.reference') }}
</el-button>
</template>
@ -42,30 +60,29 @@
</template>
<script>
import {getProjectVersions, versionEnableByProjectId} from "@/api/xpack";
import {apiScenarioAll, getApiScenarios} from "@/api/scenario";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsAsideContainer from "metersphere-frontend/src/components/MsAsideContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import MsApiScenarioModule from "../ApiScenarioModule";
import {getUUID} from "metersphere-frontend/src/utils";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import RelevanceDialog from "@/business/commons/RelevanceDialog";
import RelevanceScenarioList from "./RelevanceScenarioList";
import TestCaseRelevanceBase from "../../../commons/TestCaseRelevanceBase";
import TableSelectCountBar from "@/business/automation/scenario/api/TableSelectCountBar";
import {getProjectConfig} from "@/api/project";
import { getProjectVersions, versionEnableByProjectId } from '@/api/xpack';
import { apiScenarioAll, getApiScenarios } from '@/api/scenario';
import MsContainer from 'metersphere-frontend/src/components/MsContainer';
import MsAsideContainer from 'metersphere-frontend/src/components/MsAsideContainer';
import MsMainContainer from 'metersphere-frontend/src/components/MsMainContainer';
import MsApiScenarioModule from '../ApiScenarioModule';
import { getUUID } from 'metersphere-frontend/src/utils';
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
import RelevanceDialog from '@/business/commons/RelevanceDialog';
import RelevanceScenarioList from './RelevanceScenarioList';
import TestCaseRelevanceBase from '../../../commons/TestCaseRelevanceBase';
import TableSelectCountBar from '@/business/automation/scenario/api/TableSelectCountBar';
import { getProjectConfig } from '@/api/project';
export default {
name: "ScenarioRelevance",
name: 'ScenarioRelevance',
props: {
isAcrossSpace: {
type: Boolean,
default() {
return false;
}
}
},
},
},
components: {
TableSelectCountBar,
@ -73,7 +90,9 @@ export default {
RelevanceScenarioList,
RelevanceDialog,
MsApiScenarioModule,
MsMainContainer, MsAsideContainer, MsContainer
MsMainContainer,
MsAsideContainer,
MsContainer,
},
data() {
return {
@ -97,7 +116,7 @@ export default {
projectId(val) {
this.$refs.nodeTree.list(this.projectId);
if (val) {
getProjectConfig(val, "/SCENARIO_CUSTOM_NUM").then(result => {
getProjectConfig(val, '/SCENARIO_CUSTOM_NUM').then((result) => {
let data = result.data;
if (data) {
this.customNum = data.scenarioCustomNum;
@ -107,24 +126,24 @@ export default {
this.$refs.apiScenarioList.search(this.projectId);
this.getVersionOptionList(this.projectId);
this.checkVersionEnable(this.projectId);
}
},
},
methods: {
changeButtonLoadingType() {
this.buttonIsWorking = false;
},
createScenarioDefinition(scenarios, data, referenced) {
let emptyStepScenarios = "";
data.forEach(item => {
let emptyStepScenarios = '';
data.forEach((item) => {
if (!item.stepTotal || item.stepTotal == 0) {
emptyStepScenarios += item.name + ",";
emptyStepScenarios += item.name + ',';
} else {
let scenarioDefinition = JSON.parse(item.scenarioDefinition);
if (scenarioDefinition && scenarioDefinition.hashTree) {
let obj = {
id: item.id,
name: item.name,
type: "scenario",
type: 'scenario',
headers: scenarioDefinition.headers,
variables: scenarioDefinition.variables,
environmentMap: scenarioDefinition.environmentMap,
@ -134,16 +153,23 @@ export default {
projectId: item.projectId,
num: item.num,
versionName: item.versionName,
versionEnable: item.versionEnable
versionEnable: item.versionEnable,
};
scenarios.push(obj);
}
}
});
if (emptyStepScenarios !== "") {
if (emptyStepScenarios.endsWith(",")) {
emptyStepScenarios = emptyStepScenarios.substring(0, emptyStepScenarios.length - 1);
this.$error(this.$t('api_test.scenario.scenario_step_is_empty', [emptyStepScenarios]));
if (emptyStepScenarios !== '') {
if (emptyStepScenarios.endsWith(',')) {
emptyStepScenarios = emptyStepScenarios.substring(
0,
emptyStepScenarios.length - 1
);
this.$error(
this.$t('api_test.scenario.scenario_step_is_empty', [
emptyStepScenarios,
])
);
}
}
},
@ -152,56 +178,79 @@ export default {
let scenarios = [];
let conditions = this.getConditions();
this.currentScenarioIds.sort((a, b) => {
return conditions.tableDataIds.indexOf(a) - conditions.tableDataIds.indexOf(b);
return (
conditions.tableDataIds.indexOf(a) -
conditions.tableDataIds.indexOf(b)
);
});
if (conditions.selectAll) {
let params = {};
params.ids = this.currentScenarioIds;
params.condition = conditions;
apiScenarioAll(params).then((response) => {
this.currentScenarioIds = response.data;
if (!this.currentScenarioIds || this.currentScenarioIds.length < 1) {
this.$warning('请选择场景');
this.buttonIsWorking = false;
return;
}
this.result = getApiScenarios(this.currentScenarioIds).then(response => {
if (response.data) {
this.createScenarioDefinition(scenarios, response.data, referenced);
this.$emit('save', scenarios);
this.$refs.baseRelevance.close();
apiScenarioAll(params).then(
(response) => {
this.currentScenarioIds = response.data;
if (
!this.currentScenarioIds ||
this.currentScenarioIds.length < 1
) {
this.$warning('请选择场景');
this.buttonIsWorking = false;
return;
}
}, (error) => {
this.result = getApiScenarios(this.currentScenarioIds).then(
(response) => {
if (response.data) {
this.createScenarioDefinition(
scenarios,
response.data,
referenced
);
this.$emit('save', scenarios);
this.$refs.baseRelevance.close();
this.buttonIsWorking = false;
}
},
(error) => {
this.buttonIsWorking = false;
}
);
},
(error) => {
this.buttonIsWorking = false;
});
}, (error) => {
this.buttonIsWorking = false;
});
}
);
} else {
if (!this.currentScenarioIds || this.currentScenarioIds.length < 1) {
this.$warning('请选择场景');
this.buttonIsWorking = false;
return;
}
this.result = getApiScenarios(this.currentScenarioIds).then(response => {
if (response.data) {
this.currentScenarioIds = [];
this.createScenarioDefinition(scenarios, response.data, referenced);
this.$emit('save', scenarios);
this.$refs.baseRelevance.close();
this.result = getApiScenarios(this.currentScenarioIds).then(
(response) => {
if (response.data) {
this.currentScenarioIds = [];
this.createScenarioDefinition(
scenarios,
response.data,
referenced
);
this.$emit('save', scenarios);
this.$refs.baseRelevance.close();
this.buttonIsWorking = false;
}
},
(error) => {
this.buttonIsWorking = false;
}
}, (error) => {
this.buttonIsWorking = false;
});
);
}
},
reference() {
this.getScenarioDefinition("REF");
this.getScenarioDefinition('REF');
},
copy() {
this.getScenarioDefinition("Copy");
this.getScenarioDefinition('Copy');
},
close() {
this.$emit('close');
@ -229,8 +278,8 @@ export default {
this.$refs.apiScenarioList.search(this.projectId);
},
setData(data) {
this.currentScenario = Array.from(data).map(row => row);
this.currentScenarioIds = Array.from(data).map(row => row.id);
this.currentScenario = Array.from(data).map((row) => row);
this.currentScenarioIds = Array.from(data).map((row) => row.id);
},
setProject(projectId) {
this.projectId = projectId;
@ -240,7 +289,7 @@ export default {
},
getVersionOptionList(projectId) {
if (hasLicense()) {
getProjectVersions(projectId).then(response => {
getProjectVersions(projectId).then((response) => {
this.versionOptions = response.data;
});
}
@ -256,7 +305,7 @@ export default {
return;
}
if (hasLicense()) {
versionEnableByProjectId(projectId).then(response => {
versionEnableByProjectId(projectId).then((response) => {
this.versionEnable = false;
this.$nextTick(() => {
this.versionEnable = true;
@ -267,11 +316,9 @@ export default {
setSelectCounts(data) {
this.selectCounts = data;
this.setData(this.$refs.apiScenarioList.selectRows);
}
}
},
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -1,23 +1,21 @@
<template>
<div v-if="count && count > 0" class="content">
{{$t('commons.table.select_tip', [this.count])}}
</div>
<div v-if="count && count > 0" class="content">
{{ $t('commons.table.select_tip', [this.count]) }}
</div>
</template>
<script>
export default {
name: "TableSelectCountBar",
props: ['count']
}
export default {
name: 'TableSelectCountBar',
props: ['count'],
};
</script>
<style scoped>
.content {
text-align: center;
height: 20px;
line-height: 20px;
margin-top: 10px;
}
.content {
text-align: center;
height: 20px;
line-height: 20px;
margin-top: 10px;
}
</style>

View File

@ -158,19 +158,19 @@
</template>
<script>
import StepExtendBtns from "../component/StepExtendBtns";
import { STEP } from "../Setting";
import { useApiStore } from "@/store";
import StepExtendBtns from '../component/StepExtendBtns';
import { STEP } from '../Setting';
import { useApiStore } from '@/store';
let store = useApiStore();
export default {
name: "ApiBaseComponent",
name: 'ApiBaseComponent',
components: { StepExtendBtns },
data() {
return {
isShowInput: false,
colorStyle: "",
colorStyle: '',
stepFilter: new STEP(),
};
},
@ -201,13 +201,13 @@ export default {
color: {
type: String,
default() {
return "#B8741A";
return '#B8741A';
},
},
backgroundColor: {
type: String,
default() {
return "#F9F1EA";
return '#F9F1EA';
},
},
showCollapse: {
@ -251,7 +251,7 @@ export default {
) {
this.colorStyle = this.color;
} else {
this.colorStyle = "";
this.colorStyle = '';
}
},
},
@ -266,7 +266,7 @@ export default {
}
if (
this.data &&
this.stepFilter.get("AllSamplerProxy").indexOf(this.data.type) != -1
this.stepFilter.get('AllSamplerProxy').indexOf(this.data.type) != -1
) {
if (!this.data.method) {
this.data.method = this.data.protocol;
@ -282,62 +282,63 @@ export default {
},
isSingleButton() {
if (
this.data.type === "ConstantTimer" ||
this.data.type === "Assertions"
this.data.type === 'ConstantTimer' ||
this.data.type === 'Assertions'
) {
return (
this.innerStep &&
this.showVersion &&
this.stepFilter.get("ALlSamplerStep").indexOf(this.data.type) !== -1
this.stepFilter.get('ALlSamplerStep').indexOf(this.data.type) !== -1
);
}
return (
this.showVersion &&
this.stepFilter.get("ALlSamplerStep").indexOf(this.data.type) !== -1
this.stepFilter.get('ALlSamplerStep').indexOf(this.data.type) !== -1
);
},
isMoreButton() {
if (
this.data.type === "ConstantTimer" ||
this.data.type === "Assertions"
this.data.type === 'ConstantTimer' ||
this.data.type === 'Assertions'
) {
return (
!this.innerStep ||
(this.showBtn &&
(!this.data.disabled || this.data.root) &&
this.showVersion &&
this.stepFilter.get("ALlSamplerStep").indexOf(this.data.type) === -1)
this.stepFilter.get('ALlSamplerStep').indexOf(this.data.type) ===
-1)
);
}
return (
this.showBtn &&
(!this.data.disabled || this.data.root || this.isDeleted) &&
this.showVersion &&
this.stepFilter.get("ALlSamplerStep").indexOf(this.data.type) === -1
this.stepFilter.get('ALlSamplerStep').indexOf(this.data.type) === -1
);
},
},
methods: {
active() {
this.$emit("active");
this.$emit('active');
},
getMethod() {
if (this.data.protocol === "HTTP") {
if (this.data.protocol === 'HTTP') {
return this.data.method;
} else if (this.data.protocol === "dubbo://") {
return "DUBBO";
} else if (this.data.protocol === 'dubbo://') {
return 'DUBBO';
} else {
return this.data.protocol;
}
},
copyRow() {
this.$emit("copy");
this.$emit('copy');
},
remove() {
this.$emit("remove");
this.$emit('remove');
},
openScenario(data) {
this.$emit("openScenario", data);
this.$emit('openScenario', data);
},
editName() {
this.isShowInput = true;
@ -347,16 +348,16 @@ export default {
},
enter($event) {
if (this.showVersion) {
$event.currentTarget.className = "scenario-name";
$event.currentTarget.className = 'scenario-name';
} else {
$event.currentTarget.className = "scenario-version";
$event.currentTarget.className = 'scenario-version';
}
},
leave($event) {
if (this.showVersion) {
$event.currentTarget.className = "scenario-unscroll";
$event.currentTarget.className = 'scenario-unscroll';
} else {
$event.currentTarget.className = "scenario-version";
$event.currentTarget.className = 'scenario-version';
}
},
enable() {

View File

@ -4,167 +4,187 @@
:title="$t('load_test.runtime_config')"
width="550px"
@close="close"
:visible.sync="runModeVisible">
:visible.sync="runModeVisible"
>
<div class="env-container">
<div>
<div>{{ $t("commons.environment") }}</div>
<env-select-popover :project-ids="projectIds"
:project-list="projectList"
:case-id-env-name-map="caseIdEnvNameMap"
:is-scenario="isScenario"
:project-env-map="projectEnvListMap"
:group-id="runConfig.environmentGroupId"
@setProjectEnvMap="setProjectEnvMap"
@setEnvGroup="setEnvGroup"
ref="envSelectPopover"
class="mode-row"
<div>{{ $t('commons.environment') }}</div>
<env-select-popover
:project-ids="projectIds"
:project-list="projectList"
:case-id-env-name-map="caseIdEnvNameMap"
:is-scenario="isScenario"
:project-env-map="projectEnvListMap"
:group-id="runConfig.environmentGroupId"
@setProjectEnvMap="setProjectEnvMap"
@setEnvGroup="setEnvGroup"
ref="envSelectPopover"
class="mode-row"
></env-select-popover>
</div>
<div>
<div class="mode-row">{{ $t("run_mode.title") }}</div>
<div >
<div class="mode-row">{{ $t('run_mode.title') }}</div>
<div>
<el-radio-group
v-model="runConfig.mode"
@change="changeMode"
style="width: 100%"
class="radio-change mode-row"
>
<el-radio label="serial">{{ $t("run_mode.serial") }}</el-radio>
<el-radio label="parallel">{{ $t("run_mode.parallel") }}</el-radio>
<el-radio label="serial">{{ $t('run_mode.serial') }}</el-radio>
<el-radio label="parallel">{{ $t('run_mode.parallel') }}</el-radio>
</el-radio-group>
</div>
</div>
<!-- 资源池 -->
<div>
<div class="mode-row">{{ $t("run_mode.other_config") }}</div>
<div class="mode-row">{{ $t('run_mode.other_config') }}</div>
<div class="mode-row">
<el-radio-group v-model="runConfig.reportType">
<el-radio label="iddReport">{{ $t("run_mode.idd_report") }}</el-radio>
<el-radio label="setReport">{{ $t("run_mode.set_report") }}</el-radio>
<el-radio label="iddReport">{{
$t('run_mode.idd_report')
}}</el-radio>
<el-radio label="setReport">{{
$t('run_mode.set_report')
}}</el-radio>
</el-radio-group>
</div>
<div class="ms-mode-span-label" style="margin-top: 8px" v-if="runConfig.reportType === 'setReport'">{{ $t("run_mode.report_name") }}</div>
<div
class="ms-mode-span-label"
style="margin-top: 8px"
v-if="runConfig.reportType === 'setReport'"
>
{{ $t('run_mode.report_name') }}
</div>
<div class="mode-row" v-if="runConfig.reportType === 'setReport'">
<el-input
v-model="runConfig.reportName"
:placeholder="$t('commons.input_content')"
size="small"
style="width: 100%"/>
style="width: 100%"
/>
</div>
<div class="mode-row">
<el-checkbox v-model="runConfig.runWithinResourcePool"
:disabled="runMode === 'POOL'">
{{ $t('run_mode.run_with_resource_pool') }}
</el-checkbox><br/>
<el-select :disabled="!runConfig.runWithinResourcePool" v-model="runConfig.resourcePoolId" size="mini" class="mode-row" style="width: 100%">
<el-checkbox
v-model="runConfig.runWithinResourcePool"
:disabled="runMode === 'POOL'"
>
{{ $t('run_mode.run_with_resource_pool') }} </el-checkbox
><br />
<el-select
:disabled="!runConfig.runWithinResourcePool"
v-model="runConfig.resourcePoolId"
size="mini"
class="mode-row"
style="width: 100%"
>
<el-option
v-for="item in resourcePools"
:key="item.id"
:label="item.name"
:disabled="!item.api"
:value="item.id">
:value="item.id"
>
</el-option>
</el-select>
</div>
<!-- 失败停止 -->
<div class="mode-row" v-if="runConfig.mode === 'serial'">
<el-checkbox v-model="runConfig.onSampleError" class="radio-change">{{
$t("api_test.fail_to_stop")
}}
<el-checkbox v-model="runConfig.onSampleError" class="radio-change"
>{{ $t('api_test.fail_to_stop') }}
</el-checkbox>
</div>
</div>
</div>
</div>
<template v-slot:footer>
<ms-dialog-footer @cancel="close" @confirm="handleRunBatch"/>
<ms-dialog-footer @cancel="close" @confirm="handleRunBatch" />
</template>
</el-dialog>
</template>
<script>
import {apiScenarioEnvMap} from "@/api/scenario";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
import {strMapToObj} from "metersphere-frontend/src/utils";
import {getOwnerProjects, getProjectConfig} from "@/api/project";
import {getTestResourcePools} from "@/api/test-resource-pool";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getSystemBaseSetting} from "metersphere-frontend/src/api/system";
import EnvSelectPopover from "@/business/automation/scenario/EnvSelectPopover";
import {getApiCaseEnvironments} from "@/api/api-test-case";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import { apiScenarioEnvMap } from '@/api/scenario';
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
import { strMapToObj } from 'metersphere-frontend/src/utils';
import { getOwnerProjects, getProjectConfig } from '@/api/project';
import { getTestResourcePools } from '@/api/test-resource-pool';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { getSystemBaseSetting } from 'metersphere-frontend/src/api/system';
import EnvSelectPopover from '@/business/automation/scenario/EnvSelectPopover';
import { getApiCaseEnvironments } from '@/api/api-test-case';
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
export default {
name: "ApiRunMode",
components: {MsDialogFooter, EnvSelectPopover},
name: 'ApiRunMode',
components: { MsDialogFooter, EnvSelectPopover },
data() {
return {
runMode: "",
runMode: '',
loading: false,
runModeVisible: false,
testType: null,
resourcePools: [],
runConfig: {
reportName: "",
mode: "serial",
reportType: "iddReport",
reportName: '',
mode: 'serial',
reportType: 'iddReport',
onSampleError: false,
runWithinResourcePool: false,
resourcePoolId: null,
envMap: new Map(),
environmentGroupId: "",
environmentType: ENV_TYPE.JSON
environmentGroupId: '',
environmentType: ENV_TYPE.JSON,
},
projectEnvListMap: {},
projectList: [],
projectIds: new Set(),
caseIdEnvNameMap:{},
caseIdEnvNameMap: {},
};
},
props: {
runCaseIds:Array,
runCaseIds: Array,
request: Object,
isScenario: {
type: Boolean,
default: true
}
default: true,
},
},
watch: {
'runConfig.runWithinResourcePool'() {
if (!this.runConfig.runWithinResourcePool) {
this.runConfig = {
mode: this.runConfig.mode,
reportType: "iddReport",
reportName: "",
reportType: 'iddReport',
reportName: '',
runWithinResourcePool: false,
resourcePoolId: null,
};
}
}
},
},
methods: {
open() {
this.runModeVisible = true;
this.getResourcePools();
this.getWsProjects();
if(hasLicense()) {
if (hasLicense()) {
this.query();
}
this.showPopover();
this.runConfig.environmentType = ENV_TYPE.JSON;
},
changeMode() {
this.runConfig.reportType = "iddReport";
this.runConfig.reportName = "";
this.runConfig.reportType = 'iddReport';
this.runConfig.reportName = '';
},
close() {
this.runConfig = {
mode: "serial",
reportType: "iddReport",
reportName: "",
mode: 'serial',
reportType: 'iddReport',
reportName: '',
environmentType: ENV_TYPE.JSON,
runWithinResourcePool: false,
resourcePoolId: null,
@ -173,32 +193,42 @@ export default {
this.$emit('close');
},
getWsProjects() {
getOwnerProjects().then(res => {
getOwnerProjects().then((res) => {
this.projectList = res.data;
})
});
},
handleRunBatch() {
if ((this.runConfig.mode === 'serial' || this.runConfig.mode === 'parallel') && this.runConfig.reportType === 'setReport' && this.runConfig.reportName.trim() === "") {
if (
(this.runConfig.mode === 'serial' ||
this.runConfig.mode === 'parallel') &&
this.runConfig.reportType === 'setReport' &&
this.runConfig.reportName.trim() === ''
) {
this.$warning(this.$t('commons.input_name'));
return;
}
if (this.runConfig.runWithinResourcePool && this.runConfig.resourcePoolId == null) {
this.$warning(this.$t('workspace.env_group.please_select_run_within_resource_pool'));
if (
this.runConfig.runWithinResourcePool &&
this.runConfig.resourcePoolId == null
) {
this.$warning(
this.$t('workspace.env_group.please_select_run_within_resource_pool')
);
return;
}
this.$emit("handleRunBatch", this.runConfig);
this.$emit('handleRunBatch', this.runConfig);
this.close();
},
getResourcePools() {
this.result = getTestResourcePools().then(response => {
this.result = getTestResourcePools().then((response) => {
this.resourcePools = response.data;
});
},
query() {
this.loading = true;
this.result = getSystemBaseSetting().then(response => {
this.result = getSystemBaseSetting().then((response) => {
if (!response.data.runMode) {
response.data.runMode = 'LOCAL'
response.data.runMode = 'LOCAL';
}
this.runMode = response.data.runMode;
if (this.runMode === 'POOL') {
@ -207,10 +237,10 @@ export default {
} else {
this.loading = false;
}
})
});
},
getProjectApplication() {
getProjectConfig(getCurrentProjectID(), "").then(res => {
getProjectConfig(getCurrentProjectID(), '').then((res) => {
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
this.runConfig.resourcePoolId = res.data.resourcePoolId;
}
@ -245,7 +275,7 @@ export default {
showScenarioPopover() {
let currentProjectID = getCurrentProjectID();
this.projectIds.clear();
apiScenarioEnvMap(this.request).then(res => {
apiScenarioEnvMap(this.request).then((res) => {
let data = res.data;
this.projectEnvListMap = data;
if (data) {
@ -253,10 +283,10 @@ export default {
this.projectIds.add(d);
}
}
if (this.projectIds.size===0){
if (this.projectIds.size === 0) {
this.projectIds.add(currentProjectID);
}
this.$refs.envSelectPopover.open();
this.$refs.envSelectPopover.open();
});
},
},
@ -284,13 +314,11 @@ export default {
.ms-mode-span-label:before {
content: '*';
color: #F56C6C;
color: #f56c6c;
}
</style>
<style lang="scss" scoped>
<style lang="scss" scoped>
.radio-change:deep(.el-radio__input.is-checked + .el-radio__label) {
color: #606266 !important;
}
</style>

View File

@ -2,31 +2,86 @@
<div>
<div v-if="request.protocol === 'HTTP'">
<div v-if="isCustomizeReq">
<el-select v-model="request.method" class="ms-select" size="small" :disabled="request.disabled">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
<el-select
v-model="request.method"
class="ms-select"
size="small"
:disabled="request.disabled"
>
<el-option
v-for="item in reqOptions"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
<el-input v-model="request.domain" v-if="request.isRefEnvironment && request.domain" size="small" readonly
class="ms-input"/>
<el-input
v-model="request.domain"
v-if="request.isRefEnvironment && request.domain"
size="small"
readonly
class="ms-input"
/>
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-model="request.path"
style="width: 50%" size="small" @blur="urlChange" :disabled="request.disabled"
v-if="request.isRefEnvironment"/>
<el-input
:placeholder="$t('api_test.definition.request.path_all_info')"
v-model="request.path"
style="width: 50%"
size="small"
@blur="urlChange"
:disabled="request.disabled"
v-if="request.isRefEnvironment"
/>
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-model="request.url"
style="width: 50%" size="small" @blur="urlChange" :disabled="request.disabled" v-else/>
<el-checkbox v-if="isCustomizeReq" class="is-ref-environment" v-model="request.isRefEnvironment"
@change="setDomain" :disabled="request.disabled">
<el-input
:placeholder="$t('api_test.definition.request.path_all_info')"
v-model="request.url"
style="width: 50%"
size="small"
@blur="urlChange"
:disabled="request.disabled"
v-else
/>
<el-checkbox
v-if="isCustomizeReq"
class="is-ref-environment"
v-model="request.isRefEnvironment"
@change="setDomain"
:disabled="request.disabled"
>
{{ $t('api_test.request.refer_to_environment') }}
</el-checkbox>
</div>
<div v-else>
<el-select v-model="request.method" class="ms-select" size="small" :disabled="request.disabled">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
<el-select
v-model="request.method"
class="ms-select"
size="small"
:disabled="request.disabled"
>
<el-option
v-for="item in reqOptions"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
<el-input v-model="request.domain" v-if="request.domain" size="small" readonly class="ms-input"
:disabled="request.disabled"/>
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" style="width: 50%"
v-model="request.path" size="small" @blur="pathChange" :disabled="request.disabled"/>
<el-input
v-model="request.domain"
v-if="request.domain"
size="small"
readonly
class="ms-input"
:disabled="request.disabled"
/>
<el-input
:placeholder="$t('api_test.definition.request.path_all_info')"
style="width: 50%"
v-model="request.path"
size="small"
@blur="pathChange"
:disabled="request.disabled"
/>
</div>
</div>
@ -34,13 +89,26 @@
<el-form>
<el-row>
<el-col :span="8">
<el-form-item :label="$t('api_test.request.tcp.server')" prop="server">
<el-input class="server-input" v-model="request.server" maxlength="300" show-word-limit size="small"/>
<el-form-item
:label="$t('api_test.request.tcp.server')"
prop="server"
>
<el-input
class="server-input"
v-model="request.server"
maxlength="300"
show-word-limit
size="small"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('api_test.request.tcp.port')" prop="port" label-width="60px">
<el-input v-model="request.port" size="small"/>
<el-form-item
:label="$t('api_test.request.tcp.port')"
prop="port"
label-width="60px"
>
<el-input v-model="request.port" size="small" />
</el-form-item>
</el-col>
</el-row>
@ -50,17 +118,17 @@
</template>
<script>
import {REQ_METHOD} from "@/business/definition/model/JsonData";
import {KeyValue} from "../../../definition/model/ApiTestModel";
import { REQ_METHOD } from '@/business/definition/model/JsonData';
import { KeyValue } from '../../../definition/model/ApiTestModel';
export default {
name: "CustomizeReqInfo",
name: 'CustomizeReqInfo',
props: ['request', 'isCustomizeReq'],
data() {
return {
reqOptions: REQ_METHOD,
isUrl: false,
}
};
},
methods: {
pathChange() {
@ -68,7 +136,9 @@ export default {
if (!this.request.path || this.request.path.indexOf('?') === -1) return;
let url = this.getURL(this.addProtocol(this.request.path));
if (url) {
this.request.path = decodeURIComponent(this.request.path.substr(0, this.request.path.indexOf("?")));
this.request.path = decodeURIComponent(
this.request.path.substr(0, this.request.path.indexOf('?'))
);
}
},
urlChange() {
@ -79,17 +149,24 @@ export default {
if (!this.request.url || this.request.url.indexOf('?') === -1) return;
let url = this.getURL(this.addProtocol(this.request.url));
if (url) {
let paramUrl = this.request.url.substr(this.request.url.indexOf("?") + 1);
let paramUrl = this.request.url.substr(
this.request.url.indexOf('?') + 1
);
if (paramUrl) {
this.request.url = decodeURIComponent(this.request.url.substr(0, this.request.url.indexOf("?")));
this.request.url = decodeURIComponent(
this.request.url.substr(0, this.request.url.indexOf('?'))
);
}
}
}
},
addProtocol(url) {
if (url) {
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
return "https://" + url;
if (
!url.toLowerCase().startsWith('https') &&
!url.toLowerCase().startsWith('http')
) {
return 'https://' + url;
}
}
return url;
@ -98,17 +175,21 @@ export default {
try {
let url = new URL(urlStr);
if (url.search && url.search.length > 1) {
let params = url.search.substr(1).split("&");
params.forEach(param => {
let params = url.search.substr(1).split('&');
params.forEach((param) => {
if (param) {
let keyValues = param.split("=");
let keyValues = param.split('=');
if (keyValues) {
this.isUrl = true;
this.request.arguments.splice(0, 0, new KeyValue({
name: keyValues[0],
required: false,
value: keyValues[1]
}));
this.request.arguments.splice(
0,
0,
new KeyValue({
name: keyValues[0],
required: false,
value: keyValues[1],
})
);
}
}
});
@ -127,12 +208,12 @@ export default {
this.request.path = url.pathname;
}
} else {
this.request.path = this.request.url
this.request.path = this.request.url;
}
this.$emit("setDomain");
}
}
}
this.$emit('setDomain');
},
},
};
</script>
<style scoped>

View File

@ -1,24 +1,51 @@
<template>
<div v-loading="isReloadData || result">
<div class="jdbc-class">
<el-form :model="request" :rules="rules" ref="request" label-width="100px" :disabled="request.disabled"
style="margin: 10px">
<el-form
:model="request"
:rules="rules"
ref="request"
label-width="100px"
:disabled="request.disabled"
style="margin: 10px"
>
<el-row>
<el-col :span="8">
<el-form-item prop="environmentId" :label="$t('api_test.definition.request.run_env')">
<el-select v-model="request.environmentId" size="small" class="ms-htt-width"
:placeholder="$t('api_test.definition.request.run_env')"
@change="environmentChange" clearable :disabled="isReadOnly">
<el-option v-for="(environment, index) in environments" :key="index"
:label="environment.name"
:value="environment.id"/>
<el-button class="environment-button" size="small" type="primary" @click="openEnvironmentConfig">
<el-form-item
prop="environmentId"
:label="$t('api_test.definition.request.run_env')"
>
<el-select
v-model="request.environmentId"
size="small"
class="ms-htt-width"
:placeholder="$t('api_test.definition.request.run_env')"
@change="environmentChange"
clearable
:disabled="isReadOnly"
>
<el-option
v-for="(environment, index) in environments"
:key="index"
:label="environment.name"
:value="environment.id"
/>
<el-button
class="environment-button"
size="small"
type="primary"
@click="openEnvironmentConfig"
>
{{ $t('api_test.environment.environment_config') }}
</el-button>
<template v-slot:empty>
<div class="empty-environment">
<el-button class="environment-button" size="small" type="primary"
@click="openEnvironmentConfig">
<el-button
class="environment-button"
size="small"
type="primary"
@click="openEnvironmentConfig"
>
{{ $t('api_test.environment.environment_config') }}
</el-button>
</div>
@ -27,38 +54,83 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="$t('api_test.request.sql.dataSource')" prop="dataSourceId"
style="margin-left: 10px">
<el-select v-model="request.dataSourceId" size="small" @change="reload" :disabled="request.disabled">
<el-option v-for="(item, index) in databaseConfigsOptions" :key="index" :value="item.id"
:label="item.name"/>
<el-form-item
:label="$t('api_test.request.sql.dataSource')"
prop="dataSourceId"
style="margin-left: 10px"
>
<el-select
v-model="request.dataSourceId"
size="small"
@change="reload"
:disabled="request.disabled"
>
<el-option
v-for="(item, index) in databaseConfigsOptions"
:key="index"
:value="item.id"
:label="item.name"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="$t('api_test.request.sql.timeout')" prop="queryTimeout" style="margin-left: 10px">
<el-input-number :disabled="request.disabled" size="small" v-model="request.queryTimeout"
:placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
<el-form-item
:label="$t('api_test.request.sql.timeout')"
prop="queryTimeout"
style="margin-left: 10px"
>
<el-input-number
:disabled="request.disabled"
size="small"
v-model="request.queryTimeout"
:placeholder="$t('commons.millisecond')"
:max="1000 * 10000000"
:min="0"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('api_test.request.sql.result_variable')" prop="resultVariable">
<el-input v-model="request.resultVariable" maxlength="500" show-word-limit size="small"/>
<el-form-item
:label="$t('api_test.request.sql.result_variable')"
prop="resultVariable"
>
<el-input
v-model="request.resultVariable"
maxlength="500"
show-word-limit
size="small"
/>
</el-form-item>
<el-form-item :label="$t('api_test.request.sql.variable_names')" prop="variableNames">
<el-input v-model="request.variableNames" maxlength="500" show-word-limit size="small"/>
<el-form-item
:label="$t('api_test.request.sql.variable_names')"
prop="variableNames"
>
<el-input
v-model="request.variableNames"
maxlength="500"
show-word-limit
size="small"
/>
</el-form-item>
<el-tabs v-model="activeName" class="ms-sql-tabs">
<el-tab-pane :label="$t('api_test.scenario.variables')" name="variables">
<ms-api-scenario-variables :is-read-only="isReadOnly" :items="request.variables"
:description="$t('api_test.scenario.kv_description')"/>
<el-tab-pane
:label="$t('api_test.scenario.variables')"
name="variables"
>
<ms-api-scenario-variables
:is-read-only="isReadOnly"
:items="request.variables"
:description="$t('api_test.scenario.kv_description')"
/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.sql.sql_script')" name="sql">
<el-tab-pane
:label="$t('api_test.request.sql.sql_script')"
name="sql"
>
<ms-code-edit
:height="120"
:read-only="isReadOnly"
@ -66,40 +138,46 @@
:data.sync="request.query"
theme="eclipse"
mode="sql"
ref="codeEdit"/>
ref="codeEdit"
/>
</el-tab-pane>
</el-tabs>
</el-form>
</div>
<!-- 环境 -->
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/>
<api-environment-config
ref="environmentConfig"
@close="environmentConfigClose"
/>
</div>
</template>
<script>
import MsApiKeyValue from "@/business/definition/components/ApiKeyValue";
import MsApiExtract from "@/business/definition/components/extract/ApiExtract";
import ApiRequestMethodSelect from "@/business/definition/components/collapse/ApiRequestMethodSelect";
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
import MsApiScenarioVariables from "@/business/definition/components/ApiScenarioVariables";
import {parseEnvironment} from "@/business/environment/model/EnvironmentModel";
import ApiEnvironmentConfig from "metersphere-frontend/src/components/environment/ApiEnvironmentConfig";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getUUID, objToStrMap} from "metersphere-frontend/src/utils";
import MsJsr233Processor from "@/business/automation/scenario/component/Jsr233Processor";
import {getEnvironmentByProjectId} from "metersphere-frontend/src/api/environment";
import {useApiStore} from "@/store";
import MsApiKeyValue from '@/business/definition/components/ApiKeyValue';
import MsApiExtract from '@/business/definition/components/extract/ApiExtract';
import ApiRequestMethodSelect from '@/business/definition/components/collapse/ApiRequestMethodSelect';
import MsCodeEdit from 'metersphere-frontend/src/components/MsCodeEdit';
import MsApiScenarioVariables from '@/business/definition/components/ApiScenarioVariables';
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
import ApiEnvironmentConfig from 'metersphere-frontend/src/components/environment/ApiEnvironmentConfig';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { getUUID, objToStrMap } from 'metersphere-frontend/src/utils';
import MsJsr233Processor from '@/business/automation/scenario/component/Jsr233Processor';
import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
import { useApiStore } from '@/store';
const store = useApiStore();
export default {
name: "JdbcProcessorContent",
name: 'JdbcProcessorContent',
components: {
MsJsr233Processor,
MsApiScenarioVariables,
MsCodeEdit,
ApiRequestMethodSelect, MsApiExtract, MsApiKeyValue, ApiEnvironmentConfig
ApiRequestMethodSelect,
MsApiExtract,
MsApiKeyValue,
ApiEnvironmentConfig,
},
props: {
request: {},
@ -112,7 +190,7 @@ export default {
scenarioId: String,
isReadOnly: {
type: Boolean,
default: false
default: false,
},
},
data() {
@ -122,9 +200,9 @@ export default {
currentEnvironment: {},
databaseConfigsOptions: [],
isReloadData: false,
activeName: "variables",
activeName: 'variables',
rules: {},
}
};
},
created() {
this.getEnvironments();
@ -142,14 +220,14 @@ export default {
},
watch: {
//
'storeScenarioEnvMap': {
storeScenarioEnvMap: {
handler(v) {
this.getEnvironments();
},
deep: true
deep: true,
},
// /
'storeUseEnvironment': function () {
storeUseEnvironment: function () {
if (!this.scenarioId) {
this.getEnvironments(store.useEnvironment);
}
@ -174,43 +252,41 @@ export default {
this.reload();
},
reload() {
this.isReloadData = true
this.isReloadData = true;
this.$nextTick(() => {
this.isReloadData = false
})
this.isReloadData = false;
});
},
validate() {
this.$refs['request'].validate((valid) => {
if (valid) {
this.$emit('callback');
}
})
});
},
saveApi() {
this.basisData.method = this.basisData.protocol;
this.$emit('saveApi', this.basisData);
},
runTest() {
},
runTest() {},
itselfEnvironment(environmentId) {
let id = this.request.projectId ? this.request.projectId : this.projectId;
this.result = getEnvironmentByProjectId(id).then(response => {
this.result = getEnvironmentByProjectId(id).then((response) => {
this.environments = response.data;
let targetDataSourceName = this.request.targetDataSourceName;
this.environments.forEach(environment => {
this.environments.forEach((environment) => {
parseEnvironment(environment);
//
if (environment.id === this.request.environmentId) {
if (environment.config && environment.config.databaseConfigs) {
environment.config.databaseConfigs.forEach(item => {
environment.config.databaseConfigs.forEach((item) => {
if (item.id === this.request.dataSourceId) {
targetDataSourceName = item.name;
}
});
}
}
})
});
if (environmentId) {
this.request.environmentId = environmentId;
}
@ -218,11 +294,15 @@ export default {
});
},
getEnvironments(environmentId) {
let envId = "";
let envId = '';
let id = this.request.projectId ? this.request.projectId : this.projectId;
let scenarioEnvId = this.scenarioId !== "" ? (this.scenarioId + "_" + id) : id;
if (store.scenarioEnvMap && store.scenarioEnvMap instanceof Map
&& store.scenarioEnvMap.has(scenarioEnvId)) {
let scenarioEnvId =
this.scenarioId !== '' ? this.scenarioId + '_' + id : id;
if (
store.scenarioEnvMap &&
store.scenarioEnvMap instanceof Map &&
store.scenarioEnvMap.has(scenarioEnvId)
) {
envId = store.scenarioEnvMap.get(scenarioEnvId);
}
if (!this.scenarioId && !this.request.customizeReq) {
@ -232,29 +312,43 @@ export default {
this.environments = [];
//
if (this.request.environmentEnable && this.request.refEevMap) {
let obj = Object.prototype.toString.call(this.request.refEevMap).match(/\[object (\w+)\]/)[1].toLowerCase();
if (obj !== 'object' && obj !== "map") {
this.request.refEevMap = objToStrMap(JSON.parse(this.request.refEevMap));
} else if (obj === 'object' && obj !== "map") {
let obj = Object.prototype.toString
.call(this.request.refEevMap)
.match(/\[object (\w+)\]/)[1]
.toLowerCase();
if (obj !== 'object' && obj !== 'map') {
this.request.refEevMap = objToStrMap(
JSON.parse(this.request.refEevMap)
);
} else if (obj === 'object' && obj !== 'map') {
this.request.refEevMap = objToStrMap(this.request.refEevMap);
}
if (this.request.refEevMap instanceof Map && this.request.refEevMap.has(id)) {
if (
this.request.refEevMap instanceof Map &&
this.request.refEevMap.has(id)
) {
envId = this.request.refEevMap.get(id);
}
}
if (envId === this.request.originalEnvironmentId && this.request.originalDataSourceId) {
if (
envId === this.request.originalEnvironmentId &&
this.request.originalDataSourceId
) {
this.request.dataSourceId = this.request.originalDataSourceId;
}
let targetDataSourceName = "";
let targetDataSourceName = '';
let currentEnvironment = {};
this.result = getEnvironmentByProjectId(id).then(response => {
this.result = getEnvironmentByProjectId(id).then((response) => {
this.environments = response.data;
this.environments.forEach(environment => {
this.environments.forEach((environment) => {
parseEnvironment(environment);
//
if (environment.id === this.request.environmentId && environment.id !== envId) {
if (
environment.id === this.request.environmentId &&
environment.id !== envId
) {
if (environment.config && environment.config.databaseConfigs) {
environment.config.databaseConfigs.forEach(item => {
environment.config.databaseConfigs.forEach((item) => {
if (item.id === this.request.dataSourceId) {
targetDataSourceName = item.name;
}
@ -273,14 +367,18 @@ export default {
this.$refs.environmentConfig.open(getCurrentProjectID());
},
setStep(envId, currentEnvironment, targetDataSourceName) {
let envs = this.environments.filter(item => this.request && item.id === this.request.environmentId);
let envs = this.environments.filter(
(item) => this.request && item.id === this.request.environmentId
);
if (envs && envs.length === 0) {
let id = this.request.projectId ? this.request.projectId : this.projectId;
this.result = getEnvironmentByProjectId(id).then(response => {
let id = this.request.projectId
? this.request.projectId
: this.projectId;
this.result = getEnvironmentByProjectId(id).then((response) => {
this.environments = response.data;
this.environments.forEach(environment => {
this.environments.forEach((environment) => {
parseEnvironment(environment);
})
});
this.initDataSource(envId, currentEnvironment, targetDataSourceName);
});
} else {
@ -300,8 +398,12 @@ export default {
}
}
let flag = false;
if (currentEnvironment && currentEnvironment.config && currentEnvironment.config.databaseConfigs) {
currentEnvironment.config.databaseConfigs.forEach(item => {
if (
currentEnvironment &&
currentEnvironment.config &&
currentEnvironment.config.databaseConfigs
) {
currentEnvironment.config.databaseConfigs.forEach((item) => {
if (item.id === this.request.dataSourceId) {
flag = true;
}
@ -313,12 +415,13 @@ export default {
this.databaseConfigsOptions.push(item);
});
if (!flag && currentEnvironment.config.databaseConfigs.length > 0) {
this.request.dataSourceId = currentEnvironment.config.databaseConfigs[0].id;
this.request.dataSourceId =
currentEnvironment.config.databaseConfigs[0].id;
flag = true;
}
}
if (!flag) {
this.request.dataSourceId = "";
this.request.dataSourceId = '';
}
},
setDataSource() {
@ -333,7 +436,7 @@ export default {
},
environmentChange(value) {
this.request.dataSource = undefined;
this.request.dataSourceId = "";
this.request.dataSourceId = '';
let environment = {};
for (let i in this.environments) {
if (this.environments[i].id === value) {
@ -343,21 +446,19 @@ export default {
}
this.databaseConfigsOptions = [];
if (environment.config && environment.config.databaseConfigs) {
environment.config.databaseConfigs.forEach(item => {
environment.config.databaseConfigs.forEach((item) => {
this.databaseConfigsOptions.push(item);
})
});
}
},
environmentConfigClose() {
this.getEnvironments();
},
}
}
},
};
</script>
<style scoped>
.ms-sql-tabs {
min-height: 160px;
}
@ -388,9 +489,9 @@ export default {
}
.jdbc-class {
border: 1px #DCDFE6 solid;
border: 1px #dcdfe6 solid;
height: 100%;
border-radius: 4px;
width: 100%
width: 100%;
}
</style>

View File

@ -2,37 +2,55 @@
<div>
<el-row type="flex" :gutter="10">
<el-col :span="codeSpan" class="script-content">
<ms-code-edit v-if="isCodeEditAlive" :mode="codeEditModeMap[jsr223ProcessorData.scriptLanguage]"
:read-only="isReadOnly"
:data.sync="jsr223ProcessorData.script" theme="eclipse" :modes="['java','python']"
ref="codeEdit"/>
<ms-code-edit
v-if="isCodeEditAlive"
:mode="codeEditModeMap[jsr223ProcessorData.scriptLanguage]"
:read-only="isReadOnly"
:data.sync="jsr223ProcessorData.script"
theme="eclipse"
:modes="['java', 'python']"
ref="codeEdit"
/>
</el-col>
<div style="width: 14px;margin-right: 5px;">
<div style="height: 12px;width: 12px; line-height:12px;">
<i :class="showMenu ? 'el-icon-remove-outline' : 'el-icon-circle-plus-outline'"
class="show-menu"
@click="switchMenu"></i>
<div style="width: 14px; margin-right: 5px">
<div style="height: 12px; width: 12px; line-height: 12px">
<i
:class="
showMenu
? 'el-icon-remove-outline'
: 'el-icon-circle-plus-outline'
"
class="show-menu"
@click="switchMenu"
></i>
</div>
</div>
<el-col :span="menuSpan" class="script-index" v-if="isReadOnly !== true">
<ms-dropdown :default-command="jsr223ProcessorData.scriptLanguage" :commands="languages"
style="margin-bottom: 5px;margin-left: 15px;"
@command="languageChange"/>
<script-nav-menu ref="scriptNavMenu" :language="jsr223ProcessorData.scriptLanguage" :menus="codeTemplates"
@handleCode="handleCodeTemplate"/>
<ms-dropdown
:default-command="jsr223ProcessorData.scriptLanguage"
:commands="languages"
style="margin-bottom: 5px; margin-left: 15px"
@command="languageChange"
/>
<script-nav-menu
ref="scriptNavMenu"
:language="jsr223ProcessorData.scriptLanguage"
:menus="codeTemplates"
@handleCode="handleCodeTemplate"
/>
</el-col>
</el-row>
</div>
</template>
<script>
import MsCodeEdit from "../../../definition/components/MsCodeEdit";
import MsDropdown from "@/business/commons/MsDropdown";
import ScriptNavMenu from "./function/ScriptNavMenu";
import MsCodeEdit from '../../../definition/components/MsCodeEdit';
import MsDropdown from '@/business/commons/MsDropdown';
import ScriptNavMenu from './function/ScriptNavMenu';
export default {
name: "Jsr233ProcessorContent",
components: {MsDropdown, MsCodeEdit, ScriptNavMenu},
name: 'Jsr233ProcessorContent',
components: { MsDropdown, MsCodeEdit, ScriptNavMenu },
data() {
return {
jsr223ProcessorData: {},
@ -42,76 +60,94 @@ export default {
children: [
{
title: this.$t('project.code_segment.import_api_test'),
command: "api_definition",
command: 'api_definition',
},
{
title: this.$t('project.code_segment.new_api_test'),
command: "new_api_request",
}
]
command: 'new_api_request',
},
],
},
{
title: this.$t('project.code_segment.custom_value'),
children: [
{
title: this.$t('api_test.request.processor.code_template_get_variable'),
title: this.$t(
'api_test.request.processor.code_template_get_variable'
),
value: 'vars.get("variable_name")',
},
{
title: this.$t('api_test.request.processor.code_template_set_variable'),
title: this.$t(
'api_test.request.processor.code_template_set_variable'
),
value: 'vars.put("variable_name", "variable_value")',
},
{
title: this.$t('api_test.request.processor.code_template_get_response_header'),
title: this.$t(
'api_test.request.processor.code_template_get_response_header'
),
value: 'prev.getResponseHeaders()',
disabled: this.isPreProcessor
disabled: this.isPreProcessor,
},
{
title: this.$t('api_test.request.processor.code_template_get_response_code'),
title: this.$t(
'api_test.request.processor.code_template_get_response_code'
),
value: 'prev.getResponseCode()',
disabled: this.isPreProcessor
disabled: this.isPreProcessor,
},
{
title: this.$t('api_test.request.processor.code_template_get_response_result'),
title: this.$t(
'api_test.request.processor.code_template_get_response_result'
),
value: 'prev.getResponseDataAsString()',
disabled: this.isPreProcessor
disabled: this.isPreProcessor,
},
]
],
},
{
title: this.$t('project.code_segment.project_env'),
children: [
{
title: this.$t('api_test.request.processor.param_environment_set_global_variable'),
value: 'vars.put(${__metersphere_env_id}+"key","value");\n' + 'vars.put("key","value")',
title: this.$t(
'api_test.request.processor.param_environment_set_global_variable'
),
value:
'vars.put(${__metersphere_env_id}+"key","value");\n' +
'vars.put("key","value")',
},
]
],
},
{
title: this.$t('project.code_segment.code_segment'),
children: [
{
title: this.$t('project.code_segment.insert_segment'),
command: "custom_function",
}
]
command: 'custom_function',
},
],
},
{
title: this.$t('project.code_segment.exception_handle'),
children: [
{
title: this.$t('project.code_segment.stop_test'),
value: 'ctx.getEngine().stopThreadNow(ctx.getThread().getThreadName());'
value:
'ctx.getEngine().stopThreadNow(ctx.getThread().getThreadName());',
},
]
],
},
{
title: this.$t('project.code_segment.report_handle'),
hideScript: this.isHideScript(),
children: [
{
title: this.$t('api_test.request.processor.code_add_report_length'),
value: 'String report = ctx.getCurrentSampler().getRequestData();\n' +
title: this.$t(
'api_test.request.processor.code_add_report_length'
),
value:
'String report = ctx.getCurrentSampler().getRequestData();\n' +
'if(report!=null){\n' +
' //补足8位长度前置补0\n' +
' String reportlengthStr = String.format("%08d",report.length());\n' +
@ -120,8 +156,11 @@ export default {
'}',
},
{
title: this.$t('api_test.request.processor.code_hide_report_length'),
value: '//Get response data\n' +
title: this.$t(
'api_test.request.processor.code_hide_report_length'
),
value:
'//Get response data\n' +
'String returnData = prev.getResponseDataAsString();\n' +
'if(returnData!=null&&returnData.length()>8){\n' +
'//remove 8 report length \n' +
@ -133,13 +172,11 @@ export default {
'}',
disabled: this.isPreProcessor,
},
]
}
],
},
],
isCodeEditAlive: true,
languages: [
'beanshell', "python", "groovy", "javascript"
],
languages: ['beanshell', 'python', 'groovy', 'javascript'],
codeEditModeMap: {
beanshell: 'java',
python: 'python',
@ -151,7 +188,7 @@ export default {
codeSpan: 20,
menuSpan: 4,
showMenu: true,
}
};
},
created() {
this.jsr223ProcessorData = this.jsr223Processor;
@ -159,8 +196,7 @@ export default {
props: {
isReadOnly: {
type: Boolean,
default:
false
default: false,
},
jsr223Processor: {
type: Object,
@ -168,20 +204,19 @@ export default {
protocol: String,
isPreProcessor: {
type: Boolean,
default:
false
default: false,
},
node: {},
},
watch: {
jsr223Processor() {
this.reload();
}
},
},
methods: {
addTemplate(template) {
if (!this.jsr223ProcessorData.script) {
this.jsr223ProcessorData.script = "";
this.jsr223ProcessorData.script = '';
}
this.jsr223ProcessorData.script += template.value;
if (this.jsr223ProcessorData.scriptLanguage === 'beanshell') {
@ -198,11 +233,12 @@ export default {
},
languageChange(language) {
this.jsr223ProcessorData.scriptLanguage = language;
this.$emit("languageChange");
this.$emit('languageChange');
},
addCustomFuncScript(script) {
this.jsr223ProcessorData.script = this.jsr223ProcessorData.script ?
this.jsr223ProcessorData.script + '\n\n' + script : script;
this.jsr223ProcessorData.script = this.jsr223ProcessorData.script
? this.jsr223ProcessorData.script + '\n\n' + script
: script;
this.reload();
},
switchMenu() {
@ -220,8 +256,8 @@ export default {
this.$refs.codeEdit.insert(code);
}
},
}
}
},
};
</script>
<style scoped>
@ -268,5 +304,4 @@ export default {
.show-menu:hover {
color: #935aa1;
}
</style>

View File

@ -1,45 +1,48 @@
<template>
<span>
<el-upload
action="#"
class="api-body-upload"
list-type="picture-card"
:http-request="upload"
:beforeUpload="uploadValidate"
:file-list="plugin.files"
:on-exceed="exceed"
ref="upload">
<div class="upload-default">
<i class="el-icon-plus"/>
</div>
<div class="upload-item" slot="file" slot-scope="{file}">
<span>{{ file.file ? file.file.name : file.name }}</span>
<span class="el-upload-list__item-actions">
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
<i class="el-icon-delete"/>
</span>
</span>
</div>
</el-upload>
</span>
<span>
<el-upload
action="#"
class="api-body-upload"
list-type="picture-card"
:http-request="upload"
:beforeUpload="uploadValidate"
:file-list="plugin.files"
:on-exceed="exceed"
ref="upload"
>
<div class="upload-default">
<i class="el-icon-plus" />
</div>
<div class="upload-item" slot="file" slot-scope="{ file }">
<span>{{ file.file ? file.file.name : file.name }}</span>
<span class="el-upload-list__item-actions">
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</div>
</el-upload>
</span>
</template>
<script>
import {useApiStore} from "@/store";
import { useApiStore } from '@/store';
const store = useApiStore();
export default {
name: "MsPluginUpload",
name: 'MsPluginUpload',
data() {
return {
disabled: false,
plugin: {files: []}
plugin: { files: [] },
};
},
props: {
value: String
value: String,
},
mounted() {
if (this.value) {
@ -51,8 +54,9 @@ export default {
this.$refs.upload.handleRemove(file);
for (let i = 0; i < this.plugin.files.length; i++) {
let fileName = file.file ? file.file.name : file.name;
let paramFileName = this.plugin.files[i].file ?
this.plugin.files[i].file.name : this.plugin.files[i].name;
let paramFileName = this.plugin.files[i].file
? this.plugin.files[i].file.name
: this.plugin.files[i].name;
if (fileName === paramFileName) {
this.plugin.files.splice(i, 1);
this.$refs.upload.handleRemove(file);
@ -69,14 +73,18 @@ export default {
if (!(store.pluginFiles instanceof Array)) {
store.pluginFiles = [];
}
this.plugin.files.forEach(item => {
this.plugin.files.forEach((item) => {
if (item.file) {
files.push({uid: item.file.uid, name: item.file.name, size: item.file.size});
store.pluginFiles.push({name: item.file.name, file: item.file});
files.push({
uid: item.file.uid,
name: item.file.name,
size: item.file.size,
});
store.pluginFiles.push({ name: item.file.name, file: item.file });
} else {
files.push(item);
}
})
});
this.$emit('input', JSON.stringify(files));
},
uploadValidate(file) {
@ -91,12 +99,11 @@ export default {
if (this.plugin && !this.plugin.files) {
this.plugin.files = [];
}
}
}
},
};
</script>
<style scoped>
.el-upload {
background-color: black;
}
@ -128,12 +135,11 @@ export default {
.api-body-upload {
min-height: 30px;
border: 1px solid #EBEEF5;
border: 1px solid #ebeef5;
padding: 2px;
border-radius: 4px;
}
.upload-item {
}
</style>

View File

@ -1,60 +1,146 @@
<template>
<el-dialog :close-on-click-modal="false" :title="$t('api_test.automation.scenario_import')" width="30%"
:visible.sync="visible" class="api-import" v-loading="result" @close="close">
<el-dialog
:close-on-click-modal="false"
:title="$t('api_test.automation.scenario_import')"
width="30%"
:visible.sync="visible"
class="api-import"
v-loading="result"
@close="close"
>
<div class="header-bar">
<div>{{ $t('api_test.api_import.data_format') }}</div>
<el-radio-group v-model="selectedPlatformValue">
<el-radio v-for="(item, index) in platforms" :key="index" :label="item.value">{{ item.name }}</el-radio>
<el-radio
v-for="(item, index) in platforms"
:key="index"
:label="item.value"
>{{ item.name }}</el-radio
>
</el-radio-group>
<div class="operate-button">
<el-button class="save-button" type="primary" plain @click="save">
{{ $t('commons.save') }}
</el-button>
<el-button class="cancel-button" type="warning" plain @click="visible = false">
<el-button
class="cancel-button"
type="warning"
plain
@click="visible = false"
>
{{ $t('commons.cancel') }}
</el-button>
</div>
</div>
<el-form :model="formData" :rules="rules" label-width="105px" v-loading="result" ref="form">
<el-form
:model="formData"
:rules="rules"
label-width="105px"
v-loading="result"
ref="form"
>
<el-row>
<el-col :span="11">
<el-form-item :label="$t('commons.import_module')">
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="formData.moduleId" @getValue="setModule"
:obj="moduleObj" clearable checkStrictly/>
<ms-select-tree
size="small"
:data="moduleOptions"
:defaultKey="formData.moduleId"
@getValue="setModule"
:obj="moduleObj"
clearable
checkStrictly
/>
</el-form-item>
<el-form-item :label="$t('commons.import_mode')" prop="modeId">
<el-select size="small" v-model="formData.modeId" class="project-select" clearable>
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
<el-select
size="small"
v-model="formData.modeId"
class="project-select"
clearable
>
<el-option
v-for="item in modeOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<el-checkbox size="mini" v-if="formData.modeId === 'fullCoverage'"
v-model="formData.coverModule">
<el-checkbox
size="mini"
v-if="formData.modeId === 'fullCoverage'"
v-model="formData.coverModule"
>
{{ this.$t('commons.cover_scenario') }}
</el-checkbox>
</el-form-item>
<el-form-item v-xpack v-if="projectVersionEnable && formData.modeId === 'incrementalMerge'"
:label="$t('api_test.api_import.import_version')" prop="versionId">
<el-select size="small" v-model="formData.versionId" clearable style="width: 100%">
<el-option v-for="item in versionOptions" :key="item.id" :label="item.name" :value="item.id"/>
<el-form-item
v-xpack
v-if="
projectVersionEnable && formData.modeId === 'incrementalMerge'
"
:label="$t('api_test.api_import.import_version')"
prop="versionId"
>
<el-select
size="small"
v-model="formData.versionId"
clearable
style="width: 100%"
>
<el-option
v-for="item in versionOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item v-xpack v-if="projectVersionEnable && formData.modeId === 'fullCoverage'"
:label="$t('api_test.api_import.data_update_version')" prop="versionId">
<el-select size="small" v-model="formData.updateVersionId" clearable style="width: 100%">
<el-option v-for="item in versionOptions" :key="item.id" :label="item.name" :value="item.id"/>
<el-form-item
v-xpack
v-if="projectVersionEnable && formData.modeId === 'fullCoverage'"
:label="$t('api_test.api_import.data_update_version')"
prop="versionId"
>
<el-select
size="small"
v-model="formData.updateVersionId"
clearable
style="width: 100%"
>
<el-option
v-for="item in versionOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item v-xpack v-if="projectVersionEnable && formData.modeId === 'fullCoverage'"
:label="$t('api_test.api_import.data_new_version')" prop="versionId">
<el-select size="small" v-model="formData.versionId" clearable style="width: 100%">
<el-option v-for="item in versionOptions" :key="item.id" :label="item.name" :value="item.id"/>
<el-form-item
v-xpack
v-if="projectVersionEnable && formData.modeId === 'fullCoverage'"
:label="$t('api_test.api_import.data_new_version')"
prop="versionId"
>
<el-select
size="small"
v-model="formData.versionId"
clearable
style="width: 100%"
>
<el-option
v-for="item in versionOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="1">
<el-divider direction="vertical"/>
<el-divider direction="vertical" />
</el-col>
<el-col :span="12">
<el-upload
@ -67,32 +153,43 @@
:on-remove="handleRemove"
:file-list="fileList"
:on-exceed="handleExceed"
multiple>
multiple
>
<i class="el-icon-upload"></i>
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
<div class="el-upload__tip" slot="tip">{{ $t('api_test.api_import.file_size_limit') }}</div>
<div
class="el-upload__text"
v-html="$t('load_test.upload_tips')"
></div>
<div class="el-upload__tip" slot="tip">
{{ $t('api_test.api_import.file_size_limit') }}
</div>
</el-upload>
</el-col>
</el-row>
</el-form>
<div class="format-tip">
<div>
<span>{{ $t('api_test.api_import.tip') }}{{ selectedPlatform.tip }}</span>
<span
>{{ $t('api_test.api_import.tip') }}{{ selectedPlatform.tip }}</span
>
</div>
<div>
<span>{{ $t('api_test.api_import.export_tip') }}{{ selectedPlatform.exportTip }}</span>
<span
>{{ $t('api_test.api_import.export_tip') }}{{
selectedPlatform.exportTip
}}</span
>
</div>
<div>
<span>
{{ $t('api_test.api_import.cover_tip') }} :<br/>
{{ $t('api_test.api_import.cover_tip_scenario_1') }}<br/>
{{ $t('api_test.api_import.cover_tip_scenario_2') }}<br/>
{{ $t('api_test.api_import.cover_tip_scenario_3') }}<br/>
{{ $t('api_test.api_import.cover_tip_scenario_4') }}<br/>
{{ $t('api_test.api_import.no_cover_tip') }} :<br/>
{{ $t('api_test.api_import.no_cover_tip_scenario_1') }}<br/>
<span>
{{ $t('api_test.api_import.cover_tip') }} :<br />
{{ $t('api_test.api_import.cover_tip_scenario_1') }}<br />
{{ $t('api_test.api_import.cover_tip_scenario_2') }}<br />
{{ $t('api_test.api_import.cover_tip_scenario_3') }}<br />
{{ $t('api_test.api_import.cover_tip_scenario_4') }}<br />
{{ $t('api_test.api_import.no_cover_tip') }} :<br />
{{ $t('api_test.api_import.no_cover_tip_scenario_1') }}<br />
{{ $t('api_test.api_import.no_cover_tip_scenario_2') }}
</span>
</div>
@ -101,17 +198,20 @@
</template>
<script>
import {getProjectVersions, versionEnableByProjectId} from "@/api/xpack";
import {importScenario} from "@/api/scenario";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import {listenGoBack, removeGoBackListener} from "metersphere-frontend/src/utils";
import MsSelectTree from "metersphere-frontend/src/components/select-tree/SelectTree";
import { getProjectVersions, versionEnableByProjectId } from '@/api/xpack';
import { importScenario } from '@/api/scenario';
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
import {
listenGoBack,
removeGoBackListener,
} from 'metersphere-frontend/src/utils';
import MsSelectTree from 'metersphere-frontend/src/components/select-tree/SelectTree';
export default {
name: "ScenarioImport",
components: {MsDialogFooter, MsSelectTree},
name: 'ScenarioImport',
components: { MsDialogFooter, MsSelectTree },
props: {
saved: {
type: Boolean,
@ -125,44 +225,46 @@ export default {
swaggerUrlEable: false,
swaggerSynchronization: false,
showEnvironmentSelect: true,
modeOptions: [{
id: 'fullCoverage',
name: this.$t('commons.cover')
},
modeOptions: [
{
id: 'fullCoverage',
name: this.$t('commons.cover'),
},
{
id: 'incrementalMerge',
name: this.$t('commons.not_cover')
}],
protocol: "",
name: this.$t('commons.not_cover'),
},
],
protocol: '',
platforms: [
{
name: 'MeterSphere',
value: 'Metersphere',
tip: this.$t('api_test.api_import.ms_tip'),
exportTip: this.$t('api_test.api_import.ms_export_tip'),
suffixes: new Set(['json'])
suffixes: new Set(['json']),
},
{
name: 'Postman',
value: 'Postman',
tip: this.$t('api_test.api_import.postman_tip'),
exportTip: this.$t('api_test.api_import.post_export_tip'),
suffixes: new Set(['json'])
suffixes: new Set(['json']),
},
{
name: 'JMeter',
value: 'Jmeter',
tip: this.$t('api_test.api_import.jmeter_tip'),
exportTip: this.$t('api_test.api_import.jmeter_export_tip'),
suffixes: new Set(['jmx'])
suffixes: new Set(['jmx']),
},
{
name: 'HAR',
value: 'Har',
tip: this.$t('api_test.api_import.har_tip'),
exportTip: this.$t('api_test.api_import.har_export_tip'),
suffixes: new Set(['har'])
}
suffixes: new Set(['har']),
},
],
selectedPlatform: {},
selectedPlatformValue: 'Metersphere',
@ -175,11 +277,15 @@ export default {
swaggerUrl: '',
modeId: 'incrementalMerge',
moduleId: '',
coverModule: false
coverModule: false,
},
rules: {
modeId: [
{required: true, message: this.$t('commons.please_select_import_mode'), trigger: 'change'},
{
required: true,
message: this.$t('commons.please_select_import_mode'),
trigger: 'change',
},
],
},
currentModule: {},
@ -190,7 +296,7 @@ export default {
},
versionOptions: [],
projectVersionEnable: false,
}
};
},
created() {
this.getVersionOptions();
@ -205,7 +311,7 @@ export default {
break;
}
}
}
},
},
computed: {
projectId() {
@ -216,7 +322,7 @@ export default {
scheduleEdit() {
if (!this.formData.swaggerUrl) {
this.$warning(this.$t('commons.please_fill_path'));
this.swaggerSynchronization = !this.swaggerSynchronization
this.swaggerSynchronization = !this.swaggerSynchronization;
} else {
if (this.swaggerSynchronization) {
this.$refs.scheduleEdit.open(this.buildParam());
@ -245,7 +351,10 @@ export default {
},
uploadValidate(file, fileList) {
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
if (this.selectedPlatform.suffixes && !this.selectedPlatform.suffixes.has(suffix)) {
if (
this.selectedPlatform.suffixes &&
!this.selectedPlatform.suffixes.has(suffix)
) {
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
return false;
}
@ -258,18 +367,28 @@ export default {
save() {
localStorage.setItem('scenarioModule', this.formData.moduleId);
if (!this.formData.file) {
this.$warning("请添加一个文件");
this.$warning('请添加一个文件');
return;
}
let suffix = this.formData.file.name.substring(this.formData.file.name.lastIndexOf('.') + 1);
if (this.selectedPlatform.suffixes && !this.selectedPlatform.suffixes.has(suffix)) {
let suffix = this.formData.file.name.substring(
this.formData.file.name.lastIndexOf('.') + 1
);
if (
this.selectedPlatform.suffixes &&
!this.selectedPlatform.suffixes.has(suffix)
) {
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
return false;
}
this.$refs.form.validate(valid => {
this.$refs.form.validate((valid) => {
if (valid) {
let param = this.buildParam();
this.result = importScenario('/api/automation/import', param.file, null, this.buildParam()).then(response => {
this.result = importScenario(
'/api/automation/import',
param.file,
null,
this.buildParam()
).then((response) => {
let res = response.data;
this.$success(this.$t('test_track.case.import.success'));
this.visible = false;
@ -286,16 +405,16 @@ export default {
param.platform = this.selectedPlatformValue;
param.saved = this.saved;
if (this.currentModule) {
param.moduleId = this.formData.moduleId
this.moduleOptions.filter(item => {
param.moduleId = this.formData.moduleId;
this.moduleOptions.filter((item) => {
if (item.id === this.formData.moduleId) {
param.modulePath = item.path
param.modulePath = item.path;
}
})
param.modeId = this.formData.modeId
});
param.modeId = this.formData.modeId;
}
if (this.formData.moduleId.length === 0) {
param.moduleId = ''
param.moduleId = '';
}
param.projectId = this.projectId;
if (!this.swaggerUrlEable) {
@ -310,7 +429,7 @@ export default {
swaggerUrl: '',
modeId: this.formData.modeId,
moduleId: '',
coverModule: false
coverModule: false,
};
this.fileList = [];
removeGoBackListener(this.close);
@ -322,11 +441,14 @@ export default {
},
getVersionOptions() {
if (hasLicense()) {
getProjectVersions(getCurrentProjectID()).then(response => {
this.versionOptions = response.data.filter(v => v.status === 'open');
this.versionOptions.forEach(v => {
getProjectVersions(getCurrentProjectID()).then((response) => {
this.versionOptions = response.data.filter(
(v) => v.status === 'open'
);
this.versionOptions.forEach((v) => {
if (v.latest) {
v.name = v.name + ' ' + this.$t('api_test.api_import.latest_version');
v.name =
v.name + ' ' + this.$t('api_test.api_import.latest_version');
}
});
});
@ -337,23 +459,22 @@ export default {
return;
}
if (hasLicense()) {
versionEnableByProjectId(this.projectId).then(response => {
versionEnableByProjectId(this.projectId).then((response) => {
this.projectVersionEnable = response.data;
});
}
}
}
}
},
},
};
</script>
<style scoped>
.api-import :deep(.el-dialog) {
min-width: 700px;
}
.format-tip {
background: #EDEDED;
background: #ededed;
}
.api-upload {
@ -374,8 +495,10 @@ export default {
margin: 10px 0;
}
.header-bar, .format-tip, .el-form {
border: solid #E1E1E1 1px;
.header-bar,
.format-tip,
.el-form {
border: solid #e1e1e1 1px;
margin: 10px 0;
padding: 10px;
border-radius: 3px;
@ -414,6 +537,4 @@ export default {
.el-divider {
height: 200px;
}
</style>

View File

@ -1,12 +1,11 @@
<template>
<test-case-relevance-base
@setProject="setProject"
@save="save"
:plan-id="planId"
:dialog-title="dialogTitle"
ref="baseRelevance">
ref="baseRelevance"
>
<template v-slot:aside>
<ms-api-module
:options="options"
@ -16,7 +15,8 @@
@refreshTable="refresh"
@setModuleOptions="setModuleOptions"
:is-read-only="true"
ref="nodeTree"/>
ref="nodeTree"
/>
</template>
<relevance-api-list
@ -29,7 +29,8 @@
:is-script="isScript"
:plan-id="planId"
@isApiListEnableChange="isApiListEnableChange"
ref="apiList"/>
ref="apiList"
/>
<relevance-case-list
v-if="!isApiListEnable"
@ -41,26 +42,23 @@
:is-script="isScript"
:plan-id="planId"
@isApiListEnableChange="isApiListEnableChange"
ref="apiCaseList"/>
ref="apiCaseList"
/>
</test-case-relevance-base>
</template>
<script>
import {getApiCaseWithBLOBs} from "@/api/api-test-case";
import {apiListBatch} from "@/api/definition";
import RelevanceCaseList from "@/business/automation/scenario/api/RelevanceCaseList";
import RelevanceApiList from "@/business/automation/scenario/api/RelevanceApiList";
import MsApiModule from "@/business/definition/components/module/ApiModule";
import {getEnvironmentById} from "metersphere-frontend/src/api/environment";
import TestCaseRelevanceBase from "@/business/commons/TestCaseRelevanceBase";
import {parseEnvironment} from "@/business/environment/model/EnvironmentModel";
import { getApiCaseWithBLOBs } from '@/api/api-test-case';
import { apiListBatch } from '@/api/definition';
import RelevanceCaseList from '@/business/automation/scenario/api/RelevanceCaseList';
import RelevanceApiList from '@/business/automation/scenario/api/RelevanceApiList';
import MsApiModule from '@/business/definition/components/module/ApiModule';
import { getEnvironmentById } from 'metersphere-frontend/src/api/environment';
import TestCaseRelevanceBase from '@/business/commons/TestCaseRelevanceBase';
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
export default {
name: "ApiFuncRelevance",
name: 'ApiFuncRelevance',
components: {
RelevanceCaseList,
RelevanceApiList,
@ -79,31 +77,33 @@ export default {
isApiListEnable: true,
condition: {},
currentRow: {},
projectId: "",
options: [{value: 'HTTP', name: 'HTTP'}]
projectId: '',
options: [{ value: 'HTTP', name: 'HTTP' }],
};
},
props: {
planId: {
type: String
type: String,
},
isTestPlan: {
type: Boolean,
default() {
return true;
}
},
},
isScript: {
type: Boolean,
default() {
return false;
}
}
},
},
},
created() {
if (this.isScript) {
if (this.isApiListEnable) {
this.dialogTitle = this.$t('permission.project_api_definition.import_api');
this.dialogTitle = this.$t(
'permission.project_api_definition.import_api'
);
} else {
this.dialogTitle = this.$t('permission.project_track_case.import');
}
@ -116,12 +116,14 @@ export default {
isApiListEnable() {
if (this.isScript) {
if (this.isApiListEnable) {
this.dialogTitle = this.$t('permission.project_api_definition.import_api');
this.dialogTitle = this.$t(
'permission.project_api_definition.import_api'
);
} else {
this.dialogTitle = this.$t('permission.project_track_case.import');
}
}
}
},
},
methods: {
open() {
@ -195,10 +197,10 @@ export default {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}
getEnvironmentById(environmentId).then(response => {
getEnvironmentById(environmentId).then((response) => {
let environment = response.data;
parseEnvironment(environment);
this.$emit("save", apis, environment);
this.$emit('save', apis, environment);
});
});
} else {
@ -211,21 +213,19 @@ export default {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}
getEnvironmentById(environmentId).then(response => {
getEnvironmentById(environmentId).then((response) => {
let environment = response.data;
parseEnvironment(environment);
this.$emit("save", apiCases, environment);
this.$emit('save', apiCases, environment);
});
});
}
},
}
}
},
};
</script>
<style scoped>
:deep(.select-menu) {
margin-bottom: 15px;
}
@ -234,5 +234,4 @@ export default {
float: right;
margin-right: 10px;
}
</style>

View File

@ -1,47 +1,101 @@
<template>
<el-dialog :close-on-click-modal="false" :title="$t('project.code_segment.code_segment')" :visible.sync="visible"
:destroy-on-close="true"
@close="close" width="60%" top="10vh" v-loading="result" append-to-body class="customFunc">
<el-dialog
:close-on-click-modal="false"
:title="$t('project.code_segment.code_segment')"
:visible.sync="visible"
:destroy-on-close="true"
@close="close"
width="60%"
top="10vh"
v-loading="result"
append-to-body
class="customFunc"
>
<div>
<el-alert
:title="$t('project.code_segment.relate_tip')"
type="info"
style="width: 350px;float: left;"
:closable="false" show-icon>
style="width: 350px; float: left"
:closable="false"
show-icon
>
</el-alert>
<ms-table-search-bar :condition.sync="condition" @change="init" class="search-bar"
:tip="$t('project.code_segment.search')"/>
<el-table border class="adjust-table" :data="data" style="width: 100%" ref="table"
highlight-current-row @current-change="handleCurrentChange">
<el-table-column prop="name" :label="$t('commons.name')" show-overflow-tooltip/>
<el-table-column prop="description" :label="$t('commons.description')" show-overflow-tooltip>
<ms-table-search-bar
:condition.sync="condition"
@change="init"
class="search-bar"
:tip="$t('project.code_segment.search')"
/>
<el-table
border
class="adjust-table"
:data="data"
style="width: 100%"
ref="table"
highlight-current-row
@current-change="handleCurrentChange"
>
<el-table-column
prop="name"
:label="$t('commons.name')"
show-overflow-tooltip
/>
<el-table-column
prop="description"
:label="$t('commons.description')"
show-overflow-tooltip
>
<template v-slot:default="scope">
<pre>{{ scope.row.description }}</pre>
</template>
</el-table-column>
<el-table-column prop="tags" :label="$t('api_test.automation.tag')">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" style="margin-left: 0; margin-right: 2px">
<ms-tag
v-for="(itemName, index) in scope.row.tags"
:key="index"
type="success"
effect="plain"
:content="itemName"
style="margin-left: 0; margin-right: 2px"
>
</ms-tag>
<span></span>
</template>
</el-table-column>
<el-table-column prop="type" :label="$t('project.code_segment.language')" show-overflow-tooltip/>
<el-table-column prop="createTime"
:label="$t('commons.create_time')"
show-overflow-tooltip>
<el-table-column
prop="type"
:label="$t('project.code_segment.language')"
show-overflow-tooltip
/>
<el-table-column
prop="createTime"
:label="$t('commons.create_time')"
show-overflow-tooltip
>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="init" :current-page.sync="currentPage" :page-size.sync="pageSize" :total="total"/>
<ms-table-pagination
:change="init"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
/>
</div>
<template v-slot:footer>
<el-button @click="close" size="medium">{{ $t('commons.cancel') }}</el-button>
<el-button type="primary" @click="submit" size="medium" style="margin-left: 10px;">
<el-button @click="close" size="medium">{{
$t('commons.cancel')
}}</el-button>
<el-button
type="primary"
@click="submit"
size="medium"
style="margin-left: 10px"
>
{{ $t('commons.confirm') }}
</el-button>
</template>
@ -49,22 +103,22 @@
</template>
<script>
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import MsTag from "metersphere-frontend/src/components/MsTag";
import MsTableOperator from "metersphere-frontend/src/components/MsTableOperator";
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import MsTableSearchBar from "metersphere-frontend/src/components/MsTableSearchBar";
import {funcList, getFuncById} from "@/api/custom-func";
import MsTablePagination from 'metersphere-frontend/src/components/pagination/TablePagination';
import MsTag from 'metersphere-frontend/src/components/MsTag';
import MsTableOperator from 'metersphere-frontend/src/components/MsTableOperator';
import MsTableOperatorButton from 'metersphere-frontend/src/components/MsTableOperatorButton';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import MsTableSearchBar from 'metersphere-frontend/src/components/MsTableSearchBar';
import { funcList, getFuncById } from '@/api/custom-func';
export default {
name: "CustomFunctionRelate",
name: 'CustomFunctionRelate',
components: {
MsTablePagination,
MsTag,
MsTableOperator,
MsTableOperatorButton,
MsTableSearchBar
MsTableSearchBar,
},
data() {
return {
@ -76,8 +130,8 @@ export default {
pageSize: 10,
total: 0,
screenHeight: 'calc(100vh - 155px)',
currentRow: {}
}
currentRow: {},
};
},
methods: {
init(language) {
@ -85,16 +139,20 @@ export default {
this.condition.type = language;
}
this.condition.projectId = getCurrentProjectID();
this.result = funcList(this.currentPage, this.pageSize, this.condition).then(res => {
this.result = funcList(
this.currentPage,
this.pageSize,
this.condition
).then((res) => {
let tableData = res.data;
const {itemCount, listObject} = tableData;
const { itemCount, listObject } = tableData;
this.total = itemCount;
this.data = listObject;
this.data.forEach(item => {
this.data.forEach((item) => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
})
});
});
},
open(language) {
@ -112,23 +170,24 @@ export default {
this.$warning(this.$t('project.code_segment.select_tip'));
return;
}
this.result = getFuncById(this.currentRow.id).then(res => {
this.result = getFuncById(this.currentRow.id).then((res) => {
if (!res.data) {
this.$warning(this.$t('project.code_segment.none_content'))
this.$warning(this.$t('project.code_segment.none_content'));
}
let {script} = res.data;
this.$emit("addCustomFuncScript", script);
let { script } = res.data;
this.$emit('addCustomFuncScript', script);
this.close();
});
},
}
}
},
};
</script>
<style scoped>
pre {
margin: 0 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
Arial, sans-serif;
}
.search-bar {

View File

@ -1,100 +1,128 @@
<template>
<div style="line-height: 20px;">
<div style="line-height: 20px">
<div class="template-title">
<span class="nav-font">{{ $t('api_test.request.processor.code_template') }}</span>
<el-link href="https://jmeter.apache.org/usermanual/component_reference.html#BeanShell_PostProcessor"
target="componentReferenceDoc" style="margin-left: 30px; margin-bottom: 3px;"
type="primary"><span style="font-size: 13px;">{{ $t('commons.reference_documentation') }}</span>
<span class="nav-font">{{
$t('api_test.request.processor.code_template')
}}</span>
<el-link
href="https://jmeter.apache.org/usermanual/component_reference.html#BeanShell_PostProcessor"
target="componentReferenceDoc"
style="margin-left: 30px; margin-bottom: 3px"
type="primary"
><span style="font-size: 13px">{{
$t('commons.reference_documentation')
}}</span>
</el-link>
</div>
<div v-for="(menu, index) in menus" :key="index">
<span class="link-type" v-if="!menu.hideScript">
<i class="icon el-icon-arrow-right" style="font-weight: bold; margin-right: 2px;"
@click="active(menu)" :class="{'is-active': menu.open}"></i>
<span @click="active(menu)" class="nav-menu-title nav-font">{{ menu.title }}</span>
<i
class="icon el-icon-arrow-right"
style="font-weight: bold; margin-right: 2px"
@click="active(menu)"
:class="{ 'is-active': menu.open }"
></i>
<span @click="active(menu)" class="nav-menu-title nav-font">{{
menu.title
}}</span>
</span>
<el-collapse-transition>
<div v-if="menu.open">
<div v-for="(child, key) in menu.children" :key="key" class="func-div">
<el-link :disabled="child.disabled" @click="handleClick(child)" class="func-link nav-font">
<div
v-for="(child, key) in menu.children"
:key="key"
class="func-div"
>
<el-link
:disabled="child.disabled"
@click="handleClick(child)"
class="func-link nav-font"
>
{{ child.title }}
</el-link>
</div>
</div>
</el-collapse-transition>
</div>
<custom-function-relate ref="customFunctionRelate" @addCustomFuncScript="handleCodeTemplate"/>
<custom-function-relate
ref="customFunctionRelate"
@addCustomFuncScript="handleCodeTemplate"
/>
<!--接口列表-->
<api-func-relevance @save="apiSave" :is-test-plan="false" :is-script="true" @close="apiClose"
ref="apiFuncRelevance"/>
<api-func-relevance
@save="apiSave"
:is-test-plan="false"
:is-script="true"
@close="apiClose"
ref="apiFuncRelevance"
/>
</div>
</template>
<script>
import ApiFuncRelevance from "./ApiFuncRelevance";
import CustomFunctionRelate from "./CustomFunctionRelate";
import {getCodeTemplate} from "./custom-function";
import {SCRIPT_MENU} from "./script-menu";
import ApiFuncRelevance from './ApiFuncRelevance';
import CustomFunctionRelate from './CustomFunctionRelate';
import { getCodeTemplate } from './custom-function';
import { SCRIPT_MENU } from './script-menu';
export default {
name: "ScriptNavMenu",
name: 'ScriptNavMenu',
components: {
ApiFuncRelevance,
CustomFunctionRelate
CustomFunctionRelate,
},
data() {
return {
value: true
}
value: true,
};
},
props: {
language: {
type: String,
default() {
return "beanshell"
}
return 'beanshell';
},
},
menus: {
type: Array,
default() {
return SCRIPT_MENU
}
}
return SCRIPT_MENU;
},
},
},
methods: {
apiSave(data, env) {
// data; env:
let condition = env.config.httpConfig.conditions || [];
let protocol = "";
let host = "";
let domain = "";
let port = "";
let protocol = '';
let host = '';
let domain = '';
let port = '';
if (condition && condition.length > 0) {
//
protocol = condition[0].protocol ? condition[0].protocol : "http";
protocol = condition[0].protocol ? condition[0].protocol : 'http';
host = condition[0].socket;
domain = condition[0].domain;
port = condition[0].port;
}
// todo
if (data.length > 5) {
this.$warning("最多可以选择5个接口");
this.$warning('最多可以选择5个接口');
return;
}
let code = "";
let code = '';
if (data.length > 0) {
for (let dt of data) {
// HTTPAPI
if (dt.protocol !== "HTTP") {
if (dt.protocol !== 'HTTP') {
if (!dt.request) {
continue;
} else {
// HTTPCASE
if (dt.request) {
let req = JSON.parse(dt.request);
if (req.protocol !== "HTTP") {
if (req.protocol !== 'HTTP') {
continue;
}
}
@ -112,17 +140,17 @@ export default {
this.$refs.apiFuncRelevance.close();
},
handleCodeTemplate(code) {
this.$emit("handleCode", code);
this.$emit('handleCode', code);
},
_parseRequestObj(data) {
let requestHeaders = new Map();
let requestArguments = new Map();
let requestRest = new Map();
let requestMethod = "";
let requestBody = "";
let requestMethod = '';
let requestBody = '';
let requestBodyKvs = new Map();
let bodyType = "";
let requestPath = "";
let bodyType = '';
let requestPath = '';
let request = JSON.parse(data.request);
//
requestPath = request.path;
@ -130,14 +158,14 @@ export default {
let headers = request.headers;
let rest = request.rest;
if (rest && rest.length > 0) {
rest.forEach(r => {
rest.forEach((r) => {
if (r.enable) {
requestRest.set(r.name, r.value);
}
})
});
}
if (headers && headers.length > 0) {
headers.forEach(header => {
headers.forEach((header) => {
if (header.name) {
requestHeaders.set(header.name, header.value);
}
@ -145,29 +173,29 @@ export default {
}
let args = request.arguments;
if (args && args.length) {
args.forEach(arg => {
args.forEach((arg) => {
if (arg.name) {
requestArguments.set(arg.name, arg.value);
}
})
});
}
let body = request.body;
if (body.type === 'XML') {
requestBody = body.raw;
bodyType = "xml";
bodyType = 'xml';
} else if (body.type === 'Raw') {
requestBody = body.raw;
bodyType = "raw";
bodyType = 'raw';
} else if (body.json) {
requestBody = body.raw;
bodyType = "json";
bodyType = 'json';
} else if (body.kvs) {
bodyType = "kvs";
body.kvs.forEach(arg => {
bodyType = 'kvs';
body.kvs.forEach((arg) => {
if (arg.name) {
requestBodyKvs.set(arg.name, arg.value);
}
})
});
}
return {
requestPath,
@ -177,14 +205,12 @@ export default {
requestBodyKvs,
bodyType,
requestArguments,
requestRest
}
},
apiClose() {
requestRest,
};
},
apiClose() {},
handleClick(obj) {
let code = "";
let code = '';
if (obj.command) {
code = this._handleCommand(obj.command);
if (!code) {
@ -193,9 +219,20 @@ export default {
} else {
// todo
if (this.language !== 'beanshell' && this.language !== 'groovy') {
if (obj.title === this.$t('api_test.request.processor.code_add_report_length') ||
obj.title === this.$t('api_test.request.processor.code_hide_report_length')) {
this.$warning(this.$t('commons.no_corresponding') + " " + this.language + " " + this.$t('commons.code_template') + "");
if (
obj.title ===
this.$t('api_test.request.processor.code_add_report_length') ||
obj.title ===
this.$t('api_test.request.processor.code_hide_report_length')
) {
this.$warning(
this.$t('commons.no_corresponding') +
' ' +
this.language +
' ' +
this.$t('commons.code_template') +
''
);
return;
}
}
@ -207,33 +244,32 @@ export default {
switch (command) {
case 'custom_function':
this.$refs.customFunctionRelate.open(this.language);
return "";
return '';
case 'api_definition':
this.$refs.apiFuncRelevance.open();
return "";
return '';
case 'new_api_request': {
// requestObj
let headers = new Map();
headers.set('Content-type', 'application/json');
return getCodeTemplate(this.language, {requestHeaders: headers});
return getCodeTemplate(this.language, { requestHeaders: headers });
}
default:
return "";
return '';
}
},
active(menu) {
if (!menu.open) {
this.$set(menu, "open", true);
this.$set(menu, 'open', true);
} else {
this.$set(menu, "open", !menu.open);
this.$set(menu, 'open', !menu.open);
}
}
}
}
},
},
};
</script>
<style scoped>
.template-title {
margin-bottom: 4px;
font-weight: bold;

View File

@ -1,37 +1,45 @@
export const FUNC_TEMPLATE = {
beanshell: "",
groovy: "",
python: "",
javascript: ""
}
beanshell: '',
groovy: '',
python: '',
javascript: '',
};
export function getCodeTemplate(language, requestObj) {
switch (language) {
case "groovy":
case 'groovy':
return groovyCode(requestObj);
case "python":
case 'python':
return pythonCode(requestObj);
case "beanshell":
case 'beanshell':
return javaCode(requestObj);
case "nashornScript":
case 'nashornScript':
return jsCode(requestObj);
case "rhinoScript":
case 'rhinoScript':
return jsCode(requestObj);
case "javascript":
case 'javascript':
return jsCode(requestObj);
default:
return "";
return '';
}
}
function groovyCode(requestObj) {
let {
requestHeaders = new Map(), requestBody = "", requestPath = "", domain = "", port = "",
requestMethod = "", host = "", protocol = "", requestArguments = new Map(), requestRest = new Map(),
requestHeaders = new Map(),
requestBody = '',
requestPath = '',
domain = '',
port = '',
requestMethod = '',
host = '',
protocol = '',
requestArguments = new Map(),
requestRest = new Map(),
requestBodyKvs = new Map(),
bodyType
bodyType,
} = requestObj;
let requestUrl = "";
let requestUrl = '';
if (requestMethod.toLowerCase() === 'get' && requestBodyKvs) {
//如果是get方法要将kv值加入argument中
for (let [k, v] of requestBodyKvs) {
@ -42,49 +50,50 @@ function groovyCode(requestObj) {
let path = getMockPath(domain, port, host);
requestPath = path + replaceRestParams(requestPath, requestRest);
if (protocol && host && requestPath) {
requestUrl = protocol + "://" + domain + (port ? ":" + port : "") + requestPath;
requestUrl =
protocol + '://' + domain + (port ? ':' + port : '') + requestPath;
}
let body = JSON.stringify(requestBody);
if (requestMethod === 'POST' && bodyType === 'kvs') {
body = "\"";
body = '"';
for (let [k, v] of requestBodyKvs) {
if (body !== "\"") {
body += "&";
if (body !== '"') {
body += '&';
}
body += k + "=" + v;
body += k + '=' + v;
}
body += "\"";
body += '"';
}
if (bodyType && bodyType.toUpperCase() === 'RAW') {
requestHeaders.set("Content-type", "text/plain");
requestHeaders.set('Content-type', 'text/plain');
}
let headers = getGroovyHeaders(requestHeaders);
let obj = {requestUrl, requestMethod, headers, body};
let obj = { requestUrl, requestMethod, headers, body };
return _groovyCodeTemplate(obj);
}
function pythonCode(requestObj) {
let {
requestHeaders = new Map(),
requestBody = "",
requestPath = "/",
requestMethod = "",
host = "",
domain = "",
port = "",
protocol = "http",
requestBody = '',
requestPath = '/',
requestMethod = '',
host = '',
domain = '',
port = '',
protocol = 'http',
requestArguments = new Map(),
requestBodyKvs = new Map(),
bodyType,
requestRest = new Map()
requestRest = new Map(),
} = requestObj;
let connType = "HTTPConnection";
let connType = 'HTTPConnection';
if (protocol === 'https') {
connType = "HTTPSConnection";
connType = 'HTTPSConnection';
}
let headers = getHeaders(requestHeaders);
requestBody = requestBody ? JSON.stringify(requestBody) : "{}";
requestBody = requestBody ? JSON.stringify(requestBody) : '{}';
if (requestMethod.toLowerCase() === 'get' && requestBodyKvs) {
for (let [k, v] of requestBodyKvs) {
requestArguments.set(k, v);
@ -93,7 +102,17 @@ function pythonCode(requestObj) {
requestPath = getRequestPath(requestArguments, requestPath);
let path = getMockPath(domain, port, host);
requestPath = path + replaceRestParams(requestPath, requestRest);
let obj = {requestBody, headers, requestPath, requestMethod, requestBodyKvs, bodyType, connType, domain, port};
let obj = {
requestBody,
headers,
requestPath,
requestMethod,
requestBodyKvs,
bodyType,
connType,
domain,
port,
};
return _pythonCodeTemplate(obj);
}
@ -107,13 +126,13 @@ function jsCode(requestObj) {
function getRequestPath(requestArgs, requestPath) {
if (requestArgs.size > 0) {
requestPath = requestPath + "?"
requestPath = requestPath + '?';
let index = 1;
for (let [k, v] of requestArgs) {
if (index !== 1) {
requestPath = requestPath + "&";
requestPath = requestPath + '&';
}
requestPath = requestPath + k + "=" + v;
requestPath = requestPath + k + '=' + v;
index++;
}
}
@ -121,39 +140,53 @@ function getRequestPath(requestArgs, requestPath) {
}
function getHeaders(requestHeaders) {
let headers = "{";
let headers = '{';
let index = 1;
for (let [k, v] of requestHeaders) {
if (index !== 1) {
headers += ",";
headers += ',';
}
// 拼装
headers += `'${k}':'${v}'`;
index++;
}
headers = headers + "}"
headers = headers + '}';
return headers;
}
function getGroovyHeaders(requestHeaders) {
let headers = "[";
let headers = '[';
let index = 1;
for (let [k, v] of requestHeaders) {
if (index !== 1) {
headers += ",";
headers += ',';
}
// 拼装
headers += `'${k}':'${v}'`;
index++;
}
headers = headers + "]"
headers = headers + ']';
return headers;
}
function _pythonCodeTemplate(obj) {
let {requestBody, requestBodyKvs, bodyType, headers, requestPath, requestMethod, connType, domain, port} = obj;
let {
requestBody,
requestBodyKvs,
bodyType,
headers,
requestPath,
requestMethod,
connType,
domain,
port,
} = obj;
let reqBody = obj.requestBody;
if (requestMethod.toLowerCase() === 'post' && obj.bodyType === 'kvs' && obj.requestBodyKvs) {
if (
requestMethod.toLowerCase() === 'post' &&
obj.bodyType === 'kvs' &&
obj.requestBodyKvs
) {
reqBody = 'urllib.urlencode({';
// 设置post参数
for (let [k, v] of requestBodyKvs) {
@ -161,11 +194,12 @@ function _pythonCodeTemplate(obj) {
}
reqBody += `})`;
if (headers === '{}') {
headers = '{\'Content-type\': \'application/x-www-form-urlencoded\', \'Accept\': \'text/plain\'}';
headers =
"{'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'}";
}
}
let host = domain + (port ? ":" + port : "");
let host = domain + (port ? ':' + port : '');
return `import httplib,urllib
params = ${reqBody} # {'username':'test'}
@ -183,13 +217,13 @@ log.info(data)
}
function _groovyCodeTemplate(obj) {
let {requestUrl, requestMethod, headers, body} = obj;
let { requestUrl, requestMethod, headers, body } = obj;
let params = `[
'url': '${requestUrl}',
'method': '${requestMethod}', // POST/GET
'headers': ${headers}, // 请求headers 例:{'Content-type':'application/json'}
'data': ${body} // 参数
]`
]`;
return `import groovy.json.JsonOutput
import groovy.json.JsonSlurper
@ -220,17 +254,17 @@ log.info(conn.content.text)
function _beanshellTemplate(obj) {
let {
requestHeaders = new Map(),
requestBody = "",
requestBody = '',
requestBodyKvs = new Map(),
bodyType = "",
requestPath = "/",
requestMethod = "GET",
protocol = "http",
bodyType = '',
requestPath = '/',
requestMethod = 'GET',
protocol = 'http',
requestArguments = new Map(),
domain = "",
host = "",
port = "",
requestRest = new Map()
domain = '',
host = '',
port = '',
requestRest = new Map(),
} = obj;
let path = getMockPath(domain, port, host);
@ -241,26 +275,27 @@ function _beanshellTemplate(obj) {
.setPath("${requestPath}")
`;
// http 请求类型
let method = requestMethod.toLowerCase().replace(/^\S/, s => s.toUpperCase());
let method = requestMethod
.toLowerCase()
.replace(/^\S/, (s) => s.toUpperCase());
let httpMethodCode = `Http${method} request = new Http${method}(uri);`;
// 设置参数
for (let [k, v] of requestArguments) {
uri = uri + `.setParameter("${k}", "${v}")`;
}
if (method === "Get" && requestBodyKvs) {
if (method === 'Get' && requestBodyKvs) {
for (let [k, v] of requestBodyKvs) {
uri = uri + `.setParameter("${k}", "${v}")`;
}
}
let postKvsParam = "";
let postKvsParam = '';
if (method === 'Post') {
// 设置post参数
for (let [k, v] of requestBodyKvs) {
postKvsParam += `nameValueList.add(new BasicNameValuePair("${k}", "${v}"));\r\n`;
}
if (postKvsParam !== "") {
if (postKvsParam !== '') {
postKvsParam = `List nameValueList = new ArrayList();\r\n` + postKvsParam;
}
}
@ -275,19 +310,22 @@ function _beanshellTemplate(obj) {
uri += ` .build();`;
}
// 设置请求头
let setHeader = "";
let setHeader = '';
for (let [k, v] of requestHeaders) {
setHeader = setHeader + `request.setHeader("${k}", "${v}");` + '\n';
}
try {
requestBody = JSON.stringify(requestBody);
} catch (e) {
requestBody = "";
requestBody = '';
}
let postMethodCode = "";
if (requestMethod === "POST") {
if (bodyType === "kvs") {
postMethodCode = postKvsParam + "\r\n" + `request.setEntity(new UrlEncodedFormEntity(nameValueList, "UTF-8"));`;
let postMethodCode = '';
if (requestMethod === 'POST') {
if (bodyType === 'kvs') {
postMethodCode =
postKvsParam +
'\r\n' +
`request.setEntity(new UrlEncodedFormEntity(nameValueList, "UTF-8"));`;
} else {
postMethodCode = `request.setEntity(new StringEntity(StringEscapeUtils.unescapeJava(payload)));`;
}
@ -327,32 +365,32 @@ response = httpclient.execute(request);
if (response.getStatusLine().getStatusCode() == 200) {
String content = EntityUtils.toString(response.getEntity(), "UTF-8");
log.info(content);
}`
}`;
}
function _jsTemplate(obj) {
let {
requestHeaders = new Map(),
requestBody = "",
requestPath = "/",
requestMethod = "GET",
protocol = "http",
requestBody = '',
requestPath = '/',
requestMethod = 'GET',
protocol = 'http',
requestArguments = new Map(),
host = "",
domain = "",
port = "",
host = '',
domain = '',
port = '',
requestBodyKvs = new Map(),
bodyType = "",
requestRest = new Map()
bodyType = '',
requestRest = new Map(),
} = obj;
let url = "";
let url = '';
requestPath = replaceRestParams(requestPath, requestRest);
if (protocol && domain && port) {
let path = getMockPath(domain, port, host);
requestPath = path + requestPath;
url = protocol + "://" + domain + (port ? ":" + port : "") + requestPath;
url = protocol + '://' + domain + (port ? ':' + port : '') + requestPath;
} else if (protocol && domain) {
url = protocol + "://" + domain + requestPath;
url = protocol + '://' + domain + requestPath;
}
if (requestMethod.toLowerCase() === 'get' && requestBodyKvs) {
//如果是get方法要将kv值加入argument中
@ -364,29 +402,29 @@ function _jsTemplate(obj) {
try {
requestBody = JSON.stringify(requestBody);
} catch (e) {
requestBody = "";
requestBody = '';
}
let connStr = "";
let connStr = '';
if (bodyType && bodyType.toUpperCase() === 'RAW') {
requestHeaders.set("Content-type", "text/plain");
requestHeaders.set('Content-type', 'text/plain');
}
for (let [k, v] of requestHeaders) {
connStr += `conn.setRequestProperty("${k}","${v}");` + '\n';
}
if (requestMethod === 'POST' && bodyType === 'kvs') {
requestBody = "\"";
requestBody = '"';
for (let [k, v] of requestBodyKvs) {
if (requestBody !== "\"") {
requestBody += "&";
if (requestBody !== '"') {
requestBody += '&';
}
requestBody += k + "=" + v;
requestBody += k + '=' + v;
}
requestBody += "\"";
requestBody += '"';
}
let postParamExecCode = "";
if (requestBody && requestBody !== "" && requestBody !== "\"\"") {
let postParamExecCode = '';
if (requestBody && requestBody !== '' && requestBody !== '""') {
postParamExecCode = `
var opt = new java.io.DataOutputStream(conn.getOutputStream());
var t = (new java.lang.String(parameterData)).getBytes("utf-8");
@ -396,7 +434,6 @@ opt.close();
`;
}
return `var urlStr = "${url}"; // 请求地址
var requestMethod = "${requestMethod}"; // 请求类型
var parameterData = ${requestBody}; // 请求参数
@ -444,9 +481,9 @@ function replaceRestParams(path, restMap) {
function getMockPath(domain, port, socket) {
if (domain === socket || !port) {
return "";
return '';
}
let str = domain + ":" + port;
let str = domain + ':' + port;
// 获取socket之后的路径
return socket.substring(str.length);
}

View File

@ -1,17 +1,17 @@
import i18n from "metersphere-frontend/src/i18n";
import i18n from 'metersphere-frontend/src/i18n';
export const SCRIPT_MENU = [
{
title: i18n.t('project.code_segment.api_test'),
children: [
{
title: i18n.t('project.code_segment.import_api_test'),
command: "api_definition",
command: 'api_definition',
},
{
title: i18n.t('project.code_segment.new_api_test'),
command: "new_api_request",
}
]
command: 'new_api_request',
},
],
},
{
title: i18n.t('project.code_segment.custom_value'),
@ -25,52 +25,64 @@ export const SCRIPT_MENU = [
value: 'vars.put("variable_name", "variable_value")',
},
{
title: i18n.t('api_test.request.processor.code_template_get_response_header'),
title: i18n.t(
'api_test.request.processor.code_template_get_response_header'
),
value: 'prev.getResponseHeaders()',
},
{
title: i18n.t('api_test.request.processor.code_template_get_response_code'),
title: i18n.t(
'api_test.request.processor.code_template_get_response_code'
),
value: 'prev.getResponseCode()',
},
{
title: i18n.t('api_test.request.processor.code_template_get_response_result'),
title: i18n.t(
'api_test.request.processor.code_template_get_response_result'
),
value: 'prev.getResponseDataAsString()',
},
]
],
},
{
title: i18n.t('project.code_segment.project_env'),
children: [
{
title: i18n.t('api_test.request.processor.param_environment_set_global_variable'),
value: 'vars.put(${__metersphere_env_id}+"key","value");\n'+'vars.put("key","value")',
title: i18n.t(
'api_test.request.processor.param_environment_set_global_variable'
),
value:
'vars.put(${__metersphere_env_id}+"key","value");\n' +
'vars.put("key","value")',
},
]
],
},
{
title: i18n.t('project.code_segment.code_segment'),
children: [
{
title: i18n.t('project.code_segment.insert_segment'),
command: "custom_function",
}
]
command: 'custom_function',
},
],
},
{
title: i18n.t('project.code_segment.exception_handle'),
children: [
{
title: i18n.t('project.code_segment.stop_test'),
value: 'ctx.getEngine().stopThreadNow(ctx.getThread().getThreadName());'
title: i18n.t('project.code_segment.stop_test'),
value:
'ctx.getEngine().stopThreadNow(ctx.getThread().getThreadName());',
},
]
],
},
{
title: i18n.t('project.code_segment.report_handle'),
children: [
{
title: i18n.t('api_test.request.processor.code_add_report_length'),
value: 'String report = ctx.getCurrentSampler().getRequestData();\n' +
value:
'String report = ctx.getCurrentSampler().getRequestData();\n' +
'if(report!=null){\n' +
' //补足8位长度前置补0\n' +
' String reportlengthStr = String.format("%08d",report.length());\n' +
@ -80,7 +92,8 @@ export const SCRIPT_MENU = [
},
{
title: i18n.t('api_test.request.processor.code_hide_report_length'),
value: '//Get response data\n' +
value:
'//Get response data\n' +
'String returnData = prev.getResponseDataAsString();\n' +
'if(returnData!=null&&returnData.length()>8){\n' +
'//remove 8 report length \n' +
@ -91,6 +104,6 @@ export const SCRIPT_MENU = [
' }\n' +
'}',
},
]
}
]
],
},
];

View File

@ -17,8 +17,7 @@ const OutsideClick = {
el.__vueClickOutside__ = documentHandler;
document.addEventListener('click', documentHandler);
},
update() {
},
update() {},
unbind(el, binding) {
// 解除事件监听
document.removeEventListener('click', el.__vueClickOutside__);
@ -27,5 +26,3 @@ const OutsideClick = {
};
export default OutsideClick;

View File

@ -6,7 +6,7 @@
@active="active"
:is-show-name-input="!isDeletedOrRef"
:data="request"
:is-deleted="request.referenced==='REF' && !isShowNum"
:is-deleted="request.referenced === 'REF' && !isShowNum"
:draggable="draggable"
:color="displayColor.color"
:background-color="displayColor.backgroundColor"
@ -14,121 +14,209 @@
:show-btn="showBtn"
:show-version="showVersion"
:title="displayTitle"
:if-from-variable-advance="ifFromVariableAdvance">
<template v-slot:afterTitle v-if="(request.refType==='API'|| request.refType==='CASE')">
<span v-if="request.num" @click="clickResource(request)">{{ " ID: " + request.num + "" }}</span>
:if-from-variable-advance="ifFromVariableAdvance"
>
<template
v-slot:afterTitle
v-if="request.refType === 'API' || request.refType === 'CASE'"
>
<span v-if="request.num" @click="clickResource(request)">{{
' ID: ' + request.num + ''
}}</span>
<span v-else>
<el-tooltip class="ms-num" effect="dark"
:content="request.refType==='API'?$t('api_test.automation.scenario.api_none'):$t('api_test.automation.scenario.case_none')"
placement="top">
<i class="el-icon-warning"/>
<el-tooltip
class="ms-num"
effect="dark"
:content="
request.refType === 'API'
? $t('api_test.automation.scenario.api_none')
: $t('api_test.automation.scenario.case_none')
"
placement="top"
>
<i class="el-icon-warning" />
</el-tooltip>
</span>
<span v-xpack v-if="request.versionEnable&&showVersion">{{ $t('project.version.name') }}: {{
request.versionName
}}</span>
<span v-xpack v-if="request.versionEnable && showVersion"
>{{ $t('project.version.name') }}: {{ request.versionName }}</span
>
</template>
<template v-slot:behindHeaderLeft>
<el-tag size="small" class="ms-tag" v-if="request.referenced==='Deleted'" type="danger">
<el-tag
size="small"
class="ms-tag"
v-if="request.referenced === 'Deleted'"
type="danger"
>
{{ $t('api_test.automation.reference_deleted') }}
</el-tag>
<el-tag size="small" class="ms-tag" v-if="request.referenced==='Copy'">{{ $t('commons.copy') }}</el-tag>
<el-tag size="small" class="ms-tag" v-if="request.referenced ==='REF'">{{
$t('api_test.scenario.reference')
}}
<el-tag
size="small"
class="ms-tag"
v-if="request.referenced === 'Copy'"
>{{ $t('commons.copy') }}</el-tag
>
<el-tag size="small" class="ms-tag" v-if="request.referenced === 'REF'"
>{{ $t('api_test.scenario.reference') }}
</el-tag>
<span class="ms-tag ms-step-name-api">{{ getProjectName(request.projectId) }}</span>
<span class="ms-tag ms-step-name-api">{{
getProjectName(request.projectId)
}}</span>
</template>
<template v-slot:debugStepCode>
<span v-if="request.testing" class="ms-test-running">
<i class="el-icon-loading" style="font-size: 16px"/>
{{ $t('commons.testing') }}
</span>
<!-- 场景调试步骤增加误报判断 -->
<span class="ms-step-debug-code" :class="'ms-req-error-report'" v-if="!loading &&!request.testing && request.debug
&& request.requestResult[0] && request.requestResult[0].responseResult &&
request.requestResult[0].status==='FAKE_ERROR'">
FakeError
<span v-if="request.testing" class="ms-test-running">
<i class="el-icon-loading" style="font-size: 16px" />
{{ $t('commons.testing') }}
</span>
<span class="ms-step-debug-code"
@click="active"
:class="request.requestResult[0].success && reqSuccess?'ms-req-success':'ms-req-error'"
v-else-if="!loading &&!request.testing && request.debug && request.requestResult[0] && request.requestResult[0].responseResult">
{{ request.requestResult[0].success && reqSuccess ? 'Success' : 'Error' }}
<!-- 场景调试步骤增加误报判断 -->
<span
class="ms-step-debug-code"
:class="'ms-req-error-report'"
v-if="
!loading &&
!request.testing &&
request.debug &&
request.requestResult[0] &&
request.requestResult[0].responseResult &&
request.requestResult[0].status === 'FAKE_ERROR'
"
>
FakeError
</span>
<span
class="ms-step-debug-code"
@click="active"
:class="
request.requestResult[0].success && reqSuccess
? 'ms-req-success'
: 'ms-req-error'
"
v-else-if="
!loading &&
!request.testing &&
request.debug &&
request.requestResult[0] &&
request.requestResult[0].responseResult
"
>
{{
request.requestResult[0].success && reqSuccess ? 'Success' : 'Error'
}}
</span>
</template>
<template v-slot:button v-if="!ifFromVariableAdvance">
<el-tooltip :content="$t('api_test.run')" placement="top" v-if="!loading">
<el-button :disabled="!request.enable" @click="run" icon="el-icon-video-play" class="ms-btn" size="mini"
circle/>
<el-tooltip
:content="$t('api_test.run')"
placement="top"
v-if="!loading"
>
<el-button
:disabled="!request.enable"
@click="run"
icon="el-icon-video-play"
class="ms-btn"
size="mini"
circle
/>
</el-tooltip>
<el-tooltip :content="$t('report.stop_btn')" placement="top" :enterable="false" v-else>
<el-button @click.once="stop" size="mini" style="color:white;padding: 0 0.1px;width: 24px;height: 24px;"
class="stop-btn" circle>
<el-tooltip
:content="$t('report.stop_btn')"
placement="top"
:enterable="false"
v-else
>
<el-button
@click.once="stop"
size="mini"
style="color: white; padding: 0 0.1px; width: 24px; height: 24px"
class="stop-btn"
circle
>
<div style="transform: scale(0.66)">
<span style="margin-left: -4.5px;font-weight: bold;">STOP</span>
<span style="margin-left: -4.5px; font-weight: bold">STOP</span>
</div>
</el-button>
</el-tooltip>
</template>
<!--请求内容-->
<template v-slot:request>
<legend style="width: 100%;display:table-column">
<legend style="width: 100%; display: table-column">
<div v-if="!ifFromVariableAdvance">
<customize-req-info :is-customize-req="isCustomizeReq" :request="request" @setDomain="setDomain"/>
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<customize-req-info
:is-customize-req="isCustomizeReq"
:request="request"
@setDomain="setDomain"
/>
<p class="tip">{{ $t('api_test.definition.request.req_param') }}</p>
<ms-api-request-form
v-if="request.protocol==='HTTP' || request.type==='HTTPSamplerProxy'"
v-if="
request.protocol === 'HTTP' ||
request.type === 'HTTPSamplerProxy'
"
:scenario-definition="scenarioDefinition"
@editScenarioAdvance="editScenarioAdvance"
:isShowEnable="true"
:response="response"
:referenced="true"
:scenarioId="currentScenario.id"
:headers="request.headers "
:headers="request.headers"
:is-read-only="isCompReadOnly"
:request="request"/>
:request="request"
/>
<mx-esb-definition
v-if="request.esbDataStruct!=null"
v-if="request.esbDataStruct != null"
v-xpack
:request="request"
:response="response"
:showScript="true"
:show-pre-script="true"
:is-read-only="isCompReadOnly" ref="esbDefinition"/>
:is-read-only="isCompReadOnly"
ref="esbDefinition"
/>
<ms-tcp-format-parameters
v-if="(request.protocol==='TCP'|| request.type==='TCPSampler')&& request.esbDataStruct==null "
v-if="
(request.protocol === 'TCP' || request.type === 'TCPSampler') &&
request.esbDataStruct == null
"
:is-read-only="isCompReadOnly"
:response="response"
:show-pre-script="true"
:scenarioId="currentScenario.id"
:show-script="true" :request="request"/>
:show-script="true"
:request="request"
/>
<ms-sql-basis-parameters
v-if="request.protocol==='SQL'|| request.type==='JDBCSampler'"
v-if="
request.protocol === 'SQL' || request.type === 'JDBCSampler'
"
:request="request"
:response="response"
:scenarioId="currentScenario.id"
:is-read-only="isCompReadOnly"
:showScript="true"/>
:showScript="true"
/>
<ms-dubbo-basis-parameters
v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'"
v-if="
request.protocol === 'DUBBO' ||
request.protocol === 'dubbo://' ||
request.type === 'DubboSampler'
"
:request="request"
:scenarioId="currentScenario.id"
:response="response"
:is-read-only="isCompReadOnly"
:showScript="true"/>
:showScript="true"
/>
</div>
</legend>
</template>
<!-- 执行结果内容 -->
<template v-slot:result>
<div v-loading="loading">
<p class="tip">{{ $t('api_test.definition.request.res_param') }} </p>
<p class="tip">{{ $t('api_test.definition.request.res_param') }}</p>
<div v-if="request.backEsbDataStruct != null">
<mx-esb-definition-response
:currentProtocol="request.protocol"
@ -141,10 +229,18 @@
/>
</div>
<div v-else>
<el-tabs v-model="request.activeName" closable class="ms-tabs"
v-if="request.requestResult && request.requestResult.length > 1">
<el-tab-pane v-for="(item,i) in request.requestResult" :label="'循环'+(i+1)" :key="i"
style="margin-bottom: 5px">
<el-tabs
v-model="request.activeName"
closable
class="ms-tabs"
v-if="request.requestResult && request.requestResult.length > 1"
>
<el-tab-pane
v-for="(item, i) in request.requestResult"
:label="'循环' + (i + 1)"
:key="i"
style="margin-bottom: 5px"
>
<api-response-component
:currentProtocol="request.protocol"
:apiActive="true"
@ -156,31 +252,41 @@
:currentProtocol="request.protocol"
:apiActive="true"
:result="request.requestResult[0]"
v-else/>
v-else
/>
</div>
</div>
</template>
</api-base-component>
<ms-run :debug="true" :reportId="reportId" :run-data="runData" :env-map="environmentMap"
@runRefresh="runRefresh" @errorRefresh="errorRefresh" ref="runTest"/>
<ms-run
:debug="true"
:reportId="reportId"
:run-data="runData"
:env-map="environmentMap"
@runRefresh="runRefresh"
@errorRefresh="errorRefresh"
ref="runTest"
/>
</div>
</template>
<script>
import {getApiCaseById, getCaseById} from "@/api/api-test-case";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {getUUID} from "metersphere-frontend/src/utils";
import {getUrl} from "@/business/automation/scenario/component/urlhelper";
import {getCurrentByResourceId} from "@/api/user";
import {getOwnerProjectIds, getProject} from "@/api/project";
import {execStop} from "@/api/scenario";
import {useApiStore} from "@/store";
import {getDefinitionById} from "@/api/definition";
import { getApiCaseById, getCaseById } from '@/api/api-test-case';
import {
getCurrentProjectID,
getCurrentWorkspaceId,
} from 'metersphere-frontend/src/utils/token';
import { getUUID } from 'metersphere-frontend/src/utils';
import { getUrl } from '@/business/automation/scenario/component/urlhelper';
import { getCurrentByResourceId } from '@/api/user';
import { getOwnerProjectIds, getProject } from '@/api/project';
import { execStop } from '@/api/scenario';
import { useApiStore } from '@/store';
import { getDefinitionById } from '@/api/definition';
const store = useApiStore();
export default {
name: "MsApiComponent",
name: 'MsApiComponent',
props: {
request: {},
currentScenario: {},
@ -213,22 +319,30 @@ export default {
},
},
components: {
CustomizeReqInfo: () => import("@/business/automation/scenario/common/CustomizeReqInfo"),
ApiBaseComponent: () => import("../common/ApiBaseComponent"),
ApiResponseComponent: () => import("./ApiResponseComponent"),
MsSqlBasisParameters: () => import("../../../definition/components/request/database/BasisParameters"),
MsTcpFormatParameters: () => import("../../../definition/components/request/tcp/TcpFormatParameters"),
MsDubboBasisParameters: () => import("../../../definition/components/request/dubbo/BasisParameters"),
MsApiRequestForm: () => import("../../../definition/components/request/http/ApiHttpRequestForm"),
MsRequestResultTail: () => import("../../../definition/components/response/RequestResultTail"),
MsRun: () => import("../../../definition/components/Run"),
MxEsbDefinition: () => import("@/business/definition/components/esb/MxEsbDefinition"),
MxEsbDefinitionResponse: () => import("@/business/definition/components/esb/MxEsbDefinitionResponse")
CustomizeReqInfo: () =>
import('@/business/automation/scenario/common/CustomizeReqInfo'),
ApiBaseComponent: () => import('../common/ApiBaseComponent'),
ApiResponseComponent: () => import('./ApiResponseComponent'),
MsSqlBasisParameters: () =>
import('../../../definition/components/request/database/BasisParameters'),
MsTcpFormatParameters: () =>
import('../../../definition/components/request/tcp/TcpFormatParameters'),
MsDubboBasisParameters: () =>
import('../../../definition/components/request/dubbo/BasisParameters'),
MsApiRequestForm: () =>
import('../../../definition/components/request/http/ApiHttpRequestForm'),
MsRequestResultTail: () =>
import('../../../definition/components/response/RequestResultTail'),
MsRun: () => import('../../../definition/components/Run'),
MxEsbDefinition: () =>
import('@/business/definition/components/esb/MxEsbDefinition'),
MxEsbDefinitionResponse: () =>
import('@/business/definition/components/esb/MxEsbDefinitionResponse'),
},
data() {
return {
loading: false,
reportId: "",
reportId: '',
runData: [],
isShowInput: false,
environment: {},
@ -240,13 +354,17 @@ export default {
isShowNum: false,
response: {},
currentScenarioData: {},
}
};
},
created() {
//
if (!this.request.requestResult) {
this.request.requestResult = [{responseResult: {}}];
} else if (this.request.requestResult && Object.prototype.toString.call(this.request.requestResult) !== '[object Array]') {
this.request.requestResult = [{ responseResult: {} }];
} else if (
this.request.requestResult &&
Object.prototype.toString.call(this.request.requestResult) !==
'[object Array]'
) {
let obj = JSON.parse(JSON.stringify(this.request.requestResult));
this.request.requestResult = [obj];
}
@ -285,31 +403,31 @@ export default {
this.forStatus();
this.reload();
},
'storeCurrentApiCaseDebugLoop'() {
storeCurrentApiCaseDebugLoop() {
this.forStatus();
this.reload();
},
},
computed: {
storeCurrentApiCaseDebugLoop() {
return store.currentApiCase ? store.currentApiCase.debugLoop : "";
return store.currentApiCase ? store.currentApiCase.debugLoop : '';
},
displayColor() {
if (this.isApiImport) {
return {
color: "#F56C6C",
backgroundColor: "#FCF1F1"
}
color: '#F56C6C',
backgroundColor: '#FCF1F1',
};
} else if (this.isExternalImport) {
return {
color: "#409EFF",
backgroundColor: "#EEF5FE"
}
color: '#409EFF',
backgroundColor: '#EEF5FE',
};
} else if (this.isCustomizeReq) {
return {
color: "#008080",
backgroundColor: "#EBF2F2"
}
color: '#008080',
backgroundColor: '#EBF2F2',
};
}
return {};
},
@ -332,21 +450,27 @@ export default {
} else if (this.isCustomizeReq) {
return this.$t('api_test.automation.customize_req');
}
return "";
return '';
},
isApiImport() {
let verifies = ['Deleted', 'REF', 'Copy'];
return (this.request.referenced && verifies.indexOf(this.request.referenced) !== -1);
return (
this.request.referenced &&
verifies.indexOf(this.request.referenced) !== -1
);
},
isExternalImport() {
return (this.request.referenced && this.request.referenced === 'TO_IMPORT');
return this.request.referenced && this.request.referenced === 'TO_IMPORT';
},
isCustomizeReq() {
return (!this.request.referenced || this.request.referenced === 'Created');
return !this.request.referenced || this.request.referenced === 'Created';
},
isDeletedOrRef() {
let verifies = ['Deleted', 'REF'];
return (this.request.referenced && verifies.indexOf(this.request.referenced) !== -1);
return (
this.request.referenced &&
verifies.indexOf(this.request.referenced) !== -1
);
},
projectId() {
return getCurrentProjectID();
@ -355,11 +479,18 @@ export default {
methods: {
setOwnEnvironment(scenarioDefinition) {
for (let i in scenarioDefinition) {
let typeArray = ["JDBCPostProcessor", "JDBCSampler", "JDBCPreProcessor"]
let typeArray = [
'JDBCPostProcessor',
'JDBCSampler',
'JDBCPreProcessor',
];
if (typeArray.indexOf(scenarioDefinition[i].type) !== -1) {
scenarioDefinition[i].currentScenarioId = this.currentScenario.id;
}
if (scenarioDefinition[i].hashTree && scenarioDefinition[i].hashTree.length > 0) {
if (
scenarioDefinition[i].hashTree &&
scenarioDefinition[i].hashTree.length > 0
) {
this.setOwnEnvironment(scenarioDefinition[i].hashTree);
}
}
@ -367,22 +498,25 @@ export default {
forStatus() {
this.reqSuccess = true;
if (this.request.result && this.request.result.length > 0) {
this.request.result.forEach(item => {
item.requestResult.forEach(req => {
this.request.result.forEach((item) => {
item.requestResult.forEach((req) => {
if (!req.success) {
this.reqSuccess = req.success;
}
})
})
} else if (this.request.requestResult && this.request.requestResult.length > 1) {
this.request.requestResult.forEach(item => {
});
});
} else if (
this.request.requestResult &&
this.request.requestResult.length > 1
) {
this.request.requestResult.forEach((item) => {
if (!item.success) {
this.reqSuccess = item.success;
if (this.node && this.node.parent && this.node.parent.data) {
this.node.parent.data.code = 'ERROR';
}
}
})
});
}
if (this.request.requestResult && this.request.requestResult.length > 0) {
this.response = this.request.requestResult[0];
@ -400,7 +534,10 @@ export default {
new URL(url);
this.request.url = url;
} catch (e) {
if (url && (!url.startsWith("http://") || !url.startsWith("https://"))) {
if (
url &&
(!url.startsWith('http://') || !url.startsWith('https://'))
) {
if (!this.isCustomizeReq) {
this.request.path = url;
this.request.url = undefined;
@ -411,7 +548,11 @@ export default {
mergeHashTree(targetHashTree) {
let sourceHashTree = this.request.hashTree;
//
if (sourceHashTree && targetHashTree && sourceHashTree.length < targetHashTree.length) {
if (
sourceHashTree &&
targetHashTree &&
sourceHashTree.length < targetHashTree.length
) {
this.request.hashTree = targetHashTree;
return;
}
@ -420,7 +561,7 @@ export default {
let updateMap = new Map();
if (!sourceHashTree || sourceHashTree.length == 0) {
if (targetHashTree) {
targetHashTree.forEach(item => {
targetHashTree.forEach((item) => {
item.disabled = true;
});
this.request.hashTree = targetHashTree;
@ -452,7 +593,11 @@ export default {
}
}
//
if (!source.id && source.label !== 'SCENARIO-REF-STEP' && index < targetHashTree.length) {
if (
!source.id &&
source.label !== 'SCENARIO-REF-STEP' &&
index < targetHashTree.length
) {
Object.assign(sourceHashTree[index], targetHashTree[index]);
sourceHashTree[index].disabled = true;
sourceHashTree[index].label = '';
@ -461,19 +606,21 @@ export default {
}
}
//
delIds.forEach(item => {
const removeIndex = sourceHashTree.findIndex(d => d.id && d.id === item);
delIds.forEach((item) => {
const removeIndex = sourceHashTree.findIndex(
(d) => d.id && d.id === item
);
sourceHashTree.splice(removeIndex, 1);
})
});
//
if (targetHashTree) {
targetHashTree.forEach(item => {
targetHashTree.forEach((item) => {
if (sourceIds.indexOf(item.id) === -1) {
item.disabled = true;
this.request.hashTree.push(item);
}
})
});
}
},
sort() {
@ -495,18 +642,30 @@ export default {
run() {
this.currentScenarioData = undefined;
this.getParentVariables(this.node);
getOwnerProjectIds().then(res => {
const project = res.data.find(p => p === this.request.projectId);
getOwnerProjectIds().then((res) => {
const project = res.data.find((p) => p === this.request.projectId);
if (!project) {
this.$warning(this.$t('automation.project_no_permission'));
} else {
let selectEnvId;
//
if (this.isApiImport || this.request.isRefEnvironment) {
if (this.request.type && (this.request.type === "HTTPSamplerProxy" || this.request.type === "JDBCSampler" || this.request.type === "TCPSampler")) {
if (store.scenarioEnvMap && store.scenarioEnvMap instanceof Map
&& store.scenarioEnvMap.has((this.currentScenario.id + "_" + this.request.projectId))) {
selectEnvId = store.scenarioEnvMap.get((this.currentScenario.id + "_" + this.request.projectId));
if (
this.request.type &&
(this.request.type === 'HTTPSamplerProxy' ||
this.request.type === 'JDBCSampler' ||
this.request.type === 'TCPSampler')
) {
if (
store.scenarioEnvMap &&
store.scenarioEnvMap instanceof Map &&
store.scenarioEnvMap.has(
this.currentScenario.id + '_' + this.request.projectId
)
) {
selectEnvId = store.scenarioEnvMap.get(
this.currentScenario.id + '_' + this.request.projectId
);
this.environmentMap = this.envMap;
}
if (!selectEnvId) {
@ -531,25 +690,37 @@ export default {
//
let variables = [];
if (this.currentScenario && this.currentScenario.variables) {
variables = JSON.parse(JSON.stringify(this.currentScenario.variables));
variables = JSON.parse(
JSON.stringify(this.currentScenario.variables)
);
}
let debugData = {
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario",
variables: variables, referenced: 'Created', headers: this.currentScenario.headers,
enableCookieShare: this.enableCookieShare, environmentId: selectEnvId, hashTree: [this.request],
id: this.currentScenario.id,
name: this.currentScenario.name,
type: 'scenario',
variables: variables,
referenced: 'Created',
headers: this.currentScenario.headers,
enableCookieShare: this.enableCookieShare,
environmentId: selectEnvId,
hashTree: [this.request],
};
//
if (this.currentScenarioData && this.currentScenarioData.variableEnable && this.currentScenarioData.variables) {
if (
this.currentScenarioData &&
this.currentScenarioData.variableEnable &&
this.currentScenarioData.variables
) {
if (!debugData.variables || debugData.variables.length === 0) {
debugData.variables = this.currentScenarioData.variables;
} else if (this.currentScenarioData.variables) {
//
debugData.variables.forEach(data => {
this.currentScenarioData.variables.forEach(item => {
debugData.variables.forEach((data) => {
this.currentScenarioData.variables.forEach((item) => {
if (data.type === item.type && data.name === item.name) {
Object.assign(data, item);
}
})
});
});
}
}
@ -559,11 +730,11 @@ export default {
/*触发执行操作*/
this.reportId = getUUID();
}
})
});
},
getParentVariables(node) {
if (!this.currentScenarioData) {
if (node && node.data && node.data.type === "scenario") {
if (node && node.data && node.data.type === 'scenario') {
this.currentScenarioData = node.data;
} else {
if (node.parent && node.parent.data) {
@ -589,46 +760,47 @@ export default {
this.$emit('refReload', this.request, this.node);
},
setDomain() {
this.$emit("setDomain");
this.$emit('setDomain');
},
reload() {
this.loading = true
this.loading = true;
this.$nextTick(() => {
this.loading = false
})
this.loading = false;
});
},
getProjectName(id) {
if (this.projectId !== id) {
const project = this.projectList.find(p => p.id === id);
return project ? project.name : "";
const project = this.projectList.find((p) => p.id === id);
return project ? project.name : '';
}
},
clickResource(resource) {
let workspaceId = getCurrentWorkspaceId();
let isTurnSpace = true
let isTurnSpace = true;
if (resource.num) {
if (resource.refType === 'API') {
getDefinitionById(resource.id).then(res => {
getDefinitionById(resource.id).then((res) => {
if (res.data) {
this.getWorkspaceId(resource, res.data, isTurnSpace, workspaceId);
}
})
});
} else {
getApiCaseById(resource.id).then(res => {
getApiCaseById(resource.id).then((res) => {
if (res.data) {
this.getWorkspaceId(resource, res.data, isTurnSpace, workspaceId);
}
})
});
}
}
},
clickCase(resource) {
let uri = getUrl(resource);
let resourceId = resource.sourceId;
if (resourceId && resourceId.startsWith("\"" || resourceId.startsWith("["))) {
if (
resourceId &&
resourceId.startsWith('"' || resourceId.startsWith('['))
) {
resourceId = JSON.parse(resource.sourceId);
}
if (resourceId instanceof Array) {
@ -639,11 +811,11 @@ export default {
});
},
toPage(uri) {
let id = "new_a";
let a = document.createElement("a");
a.setAttribute("href", uri);
a.setAttribute("target", "_blank");
a.setAttribute("id", id);
let id = 'new_a';
let a = document.createElement('a');
a.setAttribute('href', uri);
a.setAttribute('target', '_blank');
a.setAttribute('id', id);
document.body.appendChild(a);
a.click();
@ -656,54 +828,53 @@ export default {
gotoTurn(resource, workspaceId, isTurnSpace) {
if (resource.refType && resource.refType === 'API') {
if (resource.protocol === 'dubbo://') {
resource.protocol = 'DUBBO'
resource.protocol = 'DUBBO';
}
let definitionData = this.$router.resolve({
name: 'ApiDefinitionWithQuery',
params: {
redirectID: getUUID(),
dataType: "api",
dataType: 'api',
dataSelectRange: 'edit:' + resource.id,
projectId: resource.projectId,
type: resource.protocol,
workspaceId: workspaceId,
}
},
});
if (isTurnSpace) {
window.open(definitionData.href, '_blank');
}
} else if (resource.refType && resource.refType === 'CASE') {
getCaseById(resource.id).then(response => {
getCaseById(resource.id).then((response) => {
if (response.data) {
response.data.sourceId = resource.resourceId;
response.data.type = resource.type;
response.data.refType = resource.refType;
response.data.workspaceId = workspaceId;
if (isTurnSpace) {
this.clickCase(response.data)
this.clickCase(response.data);
}
} else {
this.$error("接口用例已经被删除");
this.$error('接口用例已经被删除');
}
});
}
},
checkPermission(resource, workspaceId, isTurnSpace) {
getOwnerProjectIds().then(res => {
const project = res.data.find(p => p === resource.projectId);
getOwnerProjectIds().then((res) => {
const project = res.data.find((p) => p === resource.projectId);
if (!project) {
this.$warning(this.$t('commons.no_permission'));
} else {
this.gotoTurn(resource, workspaceId, isTurnSpace)
this.gotoTurn(resource, workspaceId, isTurnSpace);
}
})
});
},
getWorkspaceId(resource, data, isTurnSpace, workspaceId) {
resource.projectId = data.projectId;
if (data.projectId !== getCurrentProjectID()) {
isTurnSpace = false;
getProject(data.projectId).then(response => {
getProject(data.projectId).then((response) => {
if (response.data) {
workspaceId = response.data.workspaceId;
isTurnSpace = true;
@ -713,9 +884,9 @@ export default {
} else {
this.checkPermission(resource, workspaceId, isTurnSpace);
}
}
}
}
},
},
};
</script>
<style scoped>
@ -724,13 +895,12 @@ export default {
}
.ms-tabs :deep(.el-icon-close:before) {
content: "";
content: '';
}
.ms-btn {
padding: 5px;
background-color: #409EFF;
background-color: #409eff;
color: white;
}
@ -759,11 +929,11 @@ export default {
}
.ms-req-error {
color: #F56C6C;
color: #f56c6c;
}
.ms-req-error-report {
color: #F6972A;
color: #f6972a;
}
.ms-test-running {
@ -771,12 +941,12 @@ export default {
}
.ms-req-success {
color: #67C23A;
color: #67c23a;
}
.stop-btn {
background-color: #E62424;
border-color: #EE6161;
background-color: #e62424;
border-color: #ee6161;
color: white;
}
@ -785,5 +955,4 @@ export default {
font-size: 15px;
color: #de9d1c;
}
</style>

View File

@ -1,36 +1,49 @@
<template>
<el-card class="api-component">
<div class="header" @click="active">
<i class="icon el-icon-arrow-right" :class="{'is-active': isActive}"/>
<ms-request-metric :response="response"/>
<i class="icon el-icon-arrow-right" :class="{ 'is-active': isActive }" />
<ms-request-metric :response="response" />
</div>
<el-collapse-transition>
<div v-if="isActive">
<el-divider></el-divider>
<ms-request-result-tail :currentProtocol="currentProtocol" :show-metric="false" :response="response"/>
<ms-request-result-tail
:currentProtocol="currentProtocol"
:show-metric="false"
:response="response"
/>
</div>
</el-collapse-transition>
</el-card>
</template>
<script>
import ApiBaseComponent from "../common/ApiBaseComponent";
import MsRequestResultTail from "../../../definition/components/response/RequestResultTail";
import ElCollapseTransition from "element-ui/src/transitions/collapse-transition";
import MsRequestMetric from "../../../definition/components/response/RequestMetric";
import {getApiReportDetail} from "@/api/definition-report";
import ApiBaseComponent from '../common/ApiBaseComponent';
import MsRequestResultTail from '../../../definition/components/response/RequestResultTail';
import ElCollapseTransition from 'element-ui/src/transitions/collapse-transition';
import MsRequestMetric from '../../../definition/components/response/RequestMetric';
import { getApiReportDetail } from '@/api/definition-report';
export default {
name: "ApiResponseComponent",
components: {ElCollapseTransition, MsRequestResultTail, ApiBaseComponent, MsRequestMetric},
props: {apiItem: {}, result: {}, currentProtocol: String, apiActive: {type: Boolean, default: false}},
name: 'ApiResponseComponent',
components: {
ElCollapseTransition,
MsRequestResultTail,
ApiBaseComponent,
MsRequestMetric,
},
props: {
apiItem: {},
result: {},
currentProtocol: String,
apiActive: { type: Boolean, default: false },
},
data() {
return {
isActive: false,
response: {responseResult: {}}
}
response: { responseResult: {} },
};
},
created() {
if (!this.result || !this.result.responseResult) {
@ -39,7 +52,7 @@ export default {
this.response = this.result;
}
if (this.apiActive) {
this.isActive = false
this.isActive = false;
}
},
watch: {
@ -53,13 +66,13 @@ export default {
},
apiItem() {
this.getExecResult();
}
},
},
methods: {
getExecResult() {
//
if (this.apiItem && this.apiItem.lastResultId) {
getApiReportDetail(this.apiItem.lastResultId).then(response => {
getApiReportDetail(this.apiItem.lastResultId).then((response) => {
if (response.data && response.data.content) {
try {
let data = JSON.parse(response.data.content);
@ -77,13 +90,12 @@ export default {
},
active() {
this.isActive = !this.isActive;
}
}
}
},
},
};
</script>
<style scoped>
.header {
height: 30px;
line-height: 30px;
@ -107,5 +119,4 @@ export default {
.metric-container {
margin-left: 25px;
}
</style>

View File

@ -23,7 +23,7 @@
>
<template v-slot:afterTitle>
<span v-if="isShowNum" @click="clickResource(scenario)">{{
" ID: " + scenario.num + ""
' ID: ' + scenario.num + ''
}}</span>
<span v-else>
<el-tooltip
@ -36,7 +36,7 @@
</el-tooltip>
</span>
<span v-xpack v-if="scenario.versionEnable"
>{{ $t("project.version.name") }}: {{ scenario.versionName }}</span
>{{ $t('project.version.name') }}: {{ scenario.versionName }}</span
>
</template>
@ -47,16 +47,16 @@
v-if="scenario.referenced === 'Deleted'"
type="danger"
>
{{ $t("api_test.automation.reference_deleted") }}
{{ $t('api_test.automation.reference_deleted') }}
</el-tag>
<el-tag
size="small"
class="ms-tag"
v-if="scenario.referenced === 'Copy'"
>{{ $t("commons.copy") }}</el-tag
>{{ $t('commons.copy') }}</el-tag
>
<el-tag size="small" class="ms-tag" v-if="scenario.referenced === 'REF'"
>{{ $t("api_test.scenario.reference") }}
>{{ $t('api_test.scenario.reference') }}
</el-tag>
<span class="ms-tag ms-step-name-api">{{
getProjectName(scenario.projectId)
@ -78,7 +78,7 @@
<template v-slot:debugStepCode>
<span v-if="node.data.testing" class="ms-test-running">
<i class="el-icon-loading" style="font-size: 16px" />
{{ $t("commons.testing") }}
{{ $t('commons.testing') }}
</span>
<span
class="ms-step-debug-code"
@ -128,26 +128,26 @@
</template>
<script>
import MsSqlBasisParameters from "../../../definition/components/request/database/BasisParameters";
import MsTcpBasisParameters from "../../../definition/components/request/tcp/TcpBasisParameters";
import MsDubboBasisParameters from "../../../definition/components/request/dubbo/BasisParameters";
import MsApiRequestForm from "../../../definition/components/request/http/ApiHttpRequestForm";
import ApiBaseComponent from "../common/ApiBaseComponent";
import MsSqlBasisParameters from '../../../definition/components/request/database/BasisParameters';
import MsTcpBasisParameters from '../../../definition/components/request/tcp/TcpBasisParameters';
import MsDubboBasisParameters from '../../../definition/components/request/dubbo/BasisParameters';
import MsApiRequestForm from '../../../definition/components/request/http/ApiHttpRequestForm';
import ApiBaseComponent from '../common/ApiBaseComponent';
import {
getCurrentProjectID,
getCurrentWorkspaceId,
} from "metersphere-frontend/src/utils/token";
import { getUUID, strMapToObj } from "metersphere-frontend/src/utils";
import { STEP } from "@/business/automation/scenario/Setting";
import { getOwnerProjectIds, getProject } from "@/api/project";
} from 'metersphere-frontend/src/utils/token';
import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils';
import { STEP } from '@/business/automation/scenario/Setting';
import { getOwnerProjectIds, getProject } from '@/api/project';
import {
checkScenarioEnv,
getScenarioById,
setScenarioDomain,
} from "@/api/scenario";
} from '@/api/scenario';
export default {
name: "ApiScenarioComponent",
name: 'ApiScenarioComponent',
props: {
scenario: {},
currentScenario: {},
@ -180,16 +180,16 @@ export default {
},
watch: {
message() {
if (this.message === "STOPPED") {
if (this.message === 'STOPPED') {
this.scenario.run = false;
}
this.reload();
},
"node.data.isBatchProcess"() {
'node.data.isBatchProcess'() {
if (
this.node.data &&
this.node.data.isBatchProcess &&
this.node.data.referenced === "REF"
this.node.data.referenced === 'REF'
) {
this.node.expanded = false;
}
@ -199,7 +199,7 @@ export default {
this.isShowNum = this.scenario.num ? true : false;
if (
this.scenario.id &&
this.scenario.referenced === "REF" &&
this.scenario.referenced === 'REF' &&
!this.scenario.loaded &&
this.scenario.hashTree
) {
@ -226,15 +226,15 @@ export default {
computed: {
isDeletedOrRef() {
return (
(this.scenario.referenced && this.scenario.referenced === "Deleted") ||
this.scenario.referenced === "REF"
(this.scenario.referenced && this.scenario.referenced === 'Deleted') ||
this.scenario.referenced === 'REF'
);
},
},
methods: {
run() {
if (!this.scenario.enable) {
this.$warning(this.$t("api_test.automation.debug_message"));
this.$warning(this.$t('api_test.automation.debug_message'));
return;
}
this.scenario.run = true;
@ -261,18 +261,18 @@ export default {
runScenario.hashTree = [this.scenario];
runScenario.stepScenario = true;
this.$emit("runScenario", runScenario);
this.$emit('runScenario', runScenario);
},
stop() {
this.scenario.run = false;
this.$emit("stopScenario");
this.$emit('stopScenario');
this.reload();
},
checkEnv(val) {
checkScenarioEnv(this.scenario.id).then((res) => {
if (this.scenario.environmentEnable && !res.data) {
this.scenario.environmentEnable = false;
this.$warning(this.$t("commons.scenario_warning"));
this.$warning(this.$t('commons.scenario_warning'));
return;
}
this.setDomain(val);
@ -301,17 +301,17 @@ export default {
status.toLowerCase()[0].toUpperCase() + status.toLowerCase().substr(1)
);
}
return "";
return '';
},
remove() {
this.$emit("remove", this.scenario, this.node);
this.$emit('remove', this.scenario, this.node);
},
active() {
if (this.node) {
if (
this.node.data &&
this.node.data.isBatchProcess &&
this.node.data.referenced === "REF"
this.node.data.referenced === 'REF'
) {
this.node.expanded = false;
} else {
@ -320,20 +320,20 @@ export default {
}
if (this.scenario && this.scenario.hashTree && this.node.expanded) {
this.scenario.disabled =
this.scenario.id && this.scenario.referenced === "REF";
this.scenario.id && this.scenario.referenced === 'REF';
this.recursive(
this.scenario.hashTree,
this.scenario.projectId,
this.scenario.id && this.scenario.referenced === "REF"
this.scenario.id && this.scenario.referenced === 'REF'
);
}
this.reload();
},
copyRow() {
this.$emit("copyRow", this.scenario, this.node);
this.$emit('copyRow', this.scenario, this.node);
},
openScenario(data) {
this.$emit("openScenario", data);
this.$emit('openScenario', data);
},
reload() {
this.loading = true;
@ -347,9 +347,9 @@ export default {
arr[i].projectId = this.calcProjectId(arr[i].projectId, id);
//
let typeArray = [
"JDBCPostProcessor",
"JDBCSampler",
"JDBCPreProcessor",
'JDBCPostProcessor',
'JDBCSampler',
'JDBCPreProcessor',
];
if (typeArray.indexOf(arr[i].type) !== -1) {
arr[i].refEevMap = new Map();
@ -377,7 +377,7 @@ export default {
getProjectName(id) {
if (id !== getCurrentProjectID()) {
const project = this.projectList.find((p) => p.id === id);
return project ? project.name + " > " : "";
return project ? project.name + ' > ' : '';
}
},
clickResource(resource) {
@ -403,24 +403,24 @@ export default {
},
gotoTurn(resource, workspaceId, isTurnSpace) {
let automationData = this.$router.resolve({
name: "ApiAutomationWithQuery",
name: 'ApiAutomationWithQuery',
params: {
redirectID: getUUID(),
dataType: "scenario",
dataSelectRange: "edit:" + resource.id,
dataType: 'scenario',
dataSelectRange: 'edit:' + resource.id,
projectId: resource.projectId,
workspaceId: workspaceId,
},
});
if (isTurnSpace) {
window.open(automationData.href, "_blank");
window.open(automationData.href, '_blank');
}
},
checkPermission(resource, workspaceId, isTurnSpace) {
getOwnerProjectIds().then((res) => {
const project = res.data.find((p) => p === resource.projectId);
if (!project) {
this.$warning(this.$t("commons.no_permission"));
this.$warning(this.$t('commons.no_permission'));
} else {
this.gotoTurn(resource, workspaceId, isTurnSpace);
}

View File

@ -43,17 +43,17 @@
</template>
<script>
import MsIfController from "./IfController";
import MsTransactionController from "./TransactionController";
import {ELEMENT_TYPE} from "../Setting";
import MsApiComponent from "./ApiComponent";
import MsLoopController from "./LoopController";
import MsApiScenarioComponent from "./ApiScenarioComponent";
import JmeterElementComponent from "./JmeterElementComponent";
import PluginComponent from "./PluginComponent";
import MsIfController from './IfController';
import MsTransactionController from './TransactionController';
import { ELEMENT_TYPE } from '../Setting';
import MsApiComponent from './ApiComponent';
import MsLoopController from './LoopController';
import MsApiScenarioComponent from './ApiScenarioComponent';
import JmeterElementComponent from './JmeterElementComponent';
import PluginComponent from './PluginComponent';
export default {
name: "ComponentConfig",
name: 'ComponentConfig',
components: {
PluginComponent,
MsIfController,
@ -62,11 +62,14 @@ export default {
MsLoopController,
MsApiScenarioComponent,
JmeterElementComponent,
MsConstantTimer: () => import("./ConstantTimer"),
MsJsr233Processor: () => import("./Jsr233Processor"),
MsScenarioAssertions: () => import("../../../definition/components/assertion/ScenarioAssertions"),
MsApiExtract: () => import("../../../definition/components/extract/ApiExtract"),
MsJdbcProcessor: () => import("@/business/automation/scenario/component/JDBCProcessor")
MsConstantTimer: () => import('./ConstantTimer'),
MsJsr233Processor: () => import('./Jsr233Processor'),
MsScenarioAssertions: () =>
import('../../../definition/components/assertion/ScenarioAssertions'),
MsApiExtract: () =>
import('../../../definition/components/extract/ApiExtract'),
MsJdbcProcessor: () =>
import('@/business/automation/scenario/component/JDBCProcessor'),
},
props: {
type: String,
@ -105,24 +108,24 @@ export default {
},
data() {
return {
title: "",
titleColor: "",
backgroundColor: "",
apiId: "",
}
title: '',
titleColor: '',
backgroundColor: '',
apiId: '',
};
},
computed: {
component({type}) {
component({ type }) {
let name;
switch (type) {
case ELEMENT_TYPE.IfController:
name = "MsIfController";
name = 'MsIfController';
break;
case ELEMENT_TYPE.TransactionController:
name = "MsTransactionController";
name = 'MsTransactionController';
break;
case ELEMENT_TYPE.ConstantTimer:
name = "MsConstantTimer";
name = 'MsConstantTimer';
break;
case ELEMENT_TYPE.JSR223Processor:
name = this.getComponent(ELEMENT_TYPE.JSR223Processor);
@ -143,81 +146,86 @@ export default {
name = this.getComponent(ELEMENT_TYPE.Assertions);
break;
case ELEMENT_TYPE.Extract:
name = "MsApiExtract";
name = 'MsApiExtract';
break;
case ELEMENT_TYPE.CustomizeReq:
name = "MsApiComponent";
name = 'MsApiComponent';
break;
case ELEMENT_TYPE.LoopController:
name = "MsLoopController";
case ELEMENT_TYPE.LoopController:
name = 'MsLoopController';
break;
case ELEMENT_TYPE.scenario:
name = "MsApiScenarioComponent";
name = 'MsApiScenarioComponent';
break;
case "AuthManager":
case 'AuthManager':
break;
case "JmeterElement":
name = "JmeterElementComponent";
case 'JmeterElement':
name = 'JmeterElementComponent';
break;
case "DubboSampler":
name = "MsApiComponent";
case 'DubboSampler':
name = 'MsApiComponent';
break;
case "HTTPSamplerProxy":
name = "MsApiComponent";
case 'HTTPSamplerProxy':
name = 'MsApiComponent';
break;
case "JDBCSampler":
name = "MsApiComponent";
case 'JDBCSampler':
name = 'MsApiComponent';
break;
case "TCPSampler":
name = "MsApiComponent";
case 'TCPSampler':
name = 'MsApiComponent';
break;
default:
name = this.getComponent(ELEMENT_TYPE.Plugin);
break;
}
return name;
}
},
},
methods: {
getComponent(type) {
if (type === ELEMENT_TYPE.JSR223PreProcessor) {
this.title = this.$t('api_test.definition.request.pre_script');
this.titleColor = "#b8741a";
this.backgroundColor = "#F9F1EA";
return "MsJsr233Processor";
this.titleColor = '#b8741a';
this.backgroundColor = '#F9F1EA';
return 'MsJsr233Processor';
} else if (type === ELEMENT_TYPE.JSR223PostProcessor) {
this.title = this.$t('api_test.definition.request.post_script');
this.titleColor = "#783887";
this.backgroundColor = "#F2ECF3";
return "MsJsr233Processor";
this.titleColor = '#783887';
this.backgroundColor = '#F2ECF3';
return 'MsJsr233Processor';
}
if (type === ELEMENT_TYPE.JDBCPreProcessor) {
this.title = this.$t('api_test.definition.request.pre_sql');
this.titleColor = "#FE6F71";
this.backgroundColor = "#F2ECF3";
return "MsJdbcProcessor";
this.titleColor = '#FE6F71';
this.backgroundColor = '#F2ECF3';
return 'MsJdbcProcessor';
} else if (type === ELEMENT_TYPE.JDBCPostProcessor) {
this.title = this.$t('api_test.definition.request.post_sql');
this.titleColor = "#1483F6";
this.backgroundColor = "#F2ECF3";
return "MsJdbcProcessor";
this.titleColor = '#1483F6';
this.backgroundColor = '#F2ECF3';
return 'MsJdbcProcessor';
} else if (type === ELEMENT_TYPE.Plugin) {
this.titleColor = "#1483F6";
this.backgroundColor = "#F2ECF3";
return "PluginComponent";
this.titleColor = '#1483F6';
this.backgroundColor = '#F2ECF3';
return 'PluginComponent';
} else if (type === ELEMENT_TYPE.Assertions) {
if (this.node && this.node.parent && this.node.parent.data && this.node.parent.data.referenced === "REF") {
if (
this.node &&
this.node.parent &&
this.node.parent.data &&
this.node.parent.data.referenced === 'REF'
) {
this.apiId = this.node.parent.data.id;
this.scenario.document.nodeType = "scenario";
this.scenario.document.nodeType = 'scenario';
} else {
this.apiId = "none";
this.apiId = 'none';
}
return "MsScenarioAssertions";
return 'MsScenarioAssertions';
} else {
this.title = this.$t('api_test.automation.customize_script');
this.titleColor = "#7B4D12";
this.backgroundColor = "#F1EEE9";
return "MsJsr233Processor";
this.titleColor = '#7B4D12';
this.backgroundColor = '#F1EEE9';
return 'MsJsr233Processor';
}
},
remove(row, node) {
@ -225,7 +233,6 @@ export default {
},
copyRow(row, node) {
this.$emit('copyRow', row, node);
},
openScenario(data) {
this.$emit('openScenario', data);
@ -243,16 +250,16 @@ export default {
this.$emit('stopScenario');
},
setDomain() {
this.$emit("setDomain");
this.$emit('setDomain');
},
savePreParams(data) {
this.$emit("savePreParams", data);
this.$emit('savePreParams', data);
},
editScenarioAdvance(data) {
this.$emit('editScenarioAdvance', data);
},
}
}
},
};
</script>
<style scoped>
@ -261,5 +268,4 @@ export default {
display: block;
margin-right: 10px;
}
</style>

View File

@ -9,23 +9,29 @@
:inner-step="innerStep"
color="#67C23A"
background-color="#F2F9EE"
:title="$t('api_test.automation.wait_controller')">
:title="$t('api_test.automation.wait_controller')"
>
<template v-slot:headerLeft>
<el-input-number class="time-input" size="mini" v-model="timer.delay" :min="0" :step="1000" ref="nameInput"
:disabled="timer.disabled"/>
<el-input-number
class="time-input"
size="mini"
v-model="timer.delay"
:min="0"
:step="1000"
ref="nameInput"
:disabled="timer.disabled"
/>
ms
</template>
</api-base-component>
</template>
<script>
import ApiBaseComponent from "../common/ApiBaseComponent";
import ApiBaseComponent from '../common/ApiBaseComponent';
export default {
name: "MsConstantTimer",
components: {ApiBaseComponent},
name: 'MsConstantTimer',
components: { ApiBaseComponent },
props: {
timer: {},
innerStep: {
@ -47,7 +53,7 @@ export default {
},
},
data() {
return {}
return {};
},
created() {
this.$nextTick(() => {
@ -61,8 +67,8 @@ export default {
copyRow() {
this.$emit('copyRow', this.timer, this.node);
},
}
}
},
};
</script>
<style scoped>

View File

@ -11,29 +11,63 @@
color="#E6A23C"
background-color="#FCF6EE"
:if-from-variable-advance="ifFromVariableAdvance"
:title="$t('api_test.automation.if_controller')">
:title="$t('api_test.automation.if_controller')"
>
<template v-slot:headerLeft>
<el-input
draggable
size="mini"
v-model="controller.variable"
style="width: 12%"
:placeholder="$t('api_test.request.condition_variable')"
/>
<el-input draggable size="mini" v-model="controller.variable" style="width: 12%" :placeholder="$t('api_test.request.condition_variable')"/>
<el-select v-model="controller.operator" :placeholder="$t('commons.please_select')" size="mini"
@change="change" class="ms-select">
<el-option v-for="o in operators" :key="o.value" :label="$t(o.label)" :value="o.value"/>
<el-select
v-model="controller.operator"
:placeholder="$t('commons.please_select')"
size="mini"
@change="change"
class="ms-select"
>
<el-option
v-for="o in operators"
:key="o.value"
:label="$t(o.label)"
:value="o.value"
/>
</el-select>
<el-input draggable size="mini" v-model="controller.value" :placeholder="$t('api_test.value')" v-if="!hasEmptyOperator" class="ms-btn"/>
<el-input draggable size="mini" v-model="controller.remark" :placeholder="$t('commons.remark')" v-if="!hasEmptyOperator && !isMax" class="ms-btn"/>
<el-input
draggable
size="mini"
v-model="controller.value"
:placeholder="$t('api_test.value')"
v-if="!hasEmptyOperator"
class="ms-btn"
/>
<el-input
draggable
size="mini"
v-model="controller.remark"
:placeholder="$t('commons.remark')"
v-if="!hasEmptyOperator && !isMax"
class="ms-btn"
/>
</template>
<template v-slot:debugStepCode>
<span v-if="node.data.testing" class="ms-test-running">
<i class="el-icon-loading" style="font-size: 16px"/>
{{ $t('commons.testing') }}
</span>
<span class="ms-step-debug-code" :class="node.data.code ==='ERROR'?'ms-req-error':'ms-req-success'" v-if="!loading && !node.data.testing && node.data.debug && node.data.code">
<i class="el-icon-loading" style="font-size: 16px" />
{{ $t('commons.testing') }}
</span>
<span
class="ms-step-debug-code"
:class="node.data.code === 'ERROR' ? 'ms-req-error' : 'ms-req-success'"
v-if="
!loading && !node.data.testing && node.data.debug && node.data.code
"
>
{{ getCode() }}
</span>
</template>
@ -41,144 +75,149 @@
</template>
<script>
import ApiBaseComponent from "../common/ApiBaseComponent";
import ApiBaseComponent from '../common/ApiBaseComponent';
export default {
name: "MsIfController",
components: {ApiBaseComponent},
props: {
controller: {},
node: {},
message: String,
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
showVersion: {
type: Boolean,
default: true,
},
index: Object,
draggable: {
type: Boolean,
default: false,
},
ifFromVariableAdvance: {
type: Boolean,
default: false,
},
export default {
name: 'MsIfController',
components: { ApiBaseComponent },
props: {
controller: {},
node: {},
message: String,
isMax: {
type: Boolean,
default: false,
},
data() {
return {
loading: false,
operators: {
EQ: {
label: "commons.adv_search.operators.equals",
value: "=="
},
NE: {
label: "commons.adv_search.operators.not_equals",
value: "!="
},
LIKE: {
label: "commons.adv_search.operators.like",
value: "=~"
},
NOT_LIKE: {
label: "commons.adv_search.operators.not_like",
value: "!~"
},
GT: {
label: "commons.adv_search.operators.gt",
value: ">"
},
LT: {
label: "commons.adv_search.operators.lt",
value: "<"
},
IS_EMPTY: {
label: "commons.adv_search.operators.is_empty",
value: "is empty"
},
IS_NOT_EMPTY: {
label: "commons.adv_search.operators.is_not_empty",
value: "is not empty"
}
}
showBtn: {
type: Boolean,
default: true,
},
showVersion: {
type: Boolean,
default: true,
},
index: Object,
draggable: {
type: Boolean,
default: false,
},
ifFromVariableAdvance: {
type: Boolean,
default: false,
},
},
data() {
return {
loading: false,
operators: {
EQ: {
label: 'commons.adv_search.operators.equals',
value: '==',
},
NE: {
label: 'commons.adv_search.operators.not_equals',
value: '!=',
},
LIKE: {
label: 'commons.adv_search.operators.like',
value: '=~',
},
NOT_LIKE: {
label: 'commons.adv_search.operators.not_like',
value: '!~',
},
GT: {
label: 'commons.adv_search.operators.gt',
value: '>',
},
LT: {
label: 'commons.adv_search.operators.lt',
value: '<',
},
IS_EMPTY: {
label: 'commons.adv_search.operators.is_empty',
value: 'is empty',
},
IS_NOT_EMPTY: {
label: 'commons.adv_search.operators.is_not_empty',
value: 'is not empty',
},
},
};
},
watch: {
message() {
this.reload();
},
},
methods: {
reload() {
this.loading = true;
this.$nextTick(() => {
this.loading = false;
});
},
getCode() {
if (this.node && this.node.data.code && this.node.data.debug) {
let status = this.node.data.code;
return (
status.toLowerCase()[0].toUpperCase() + status.toLowerCase().substr(1)
);
}
return '';
},
remove() {
this.$emit('remove', this.controller, this.node);
},
copyRow() {
this.$emit('copyRow', this.controller, this.node);
},
change(value) {
if (value.indexOf('empty') > 0 && !!this.controller.value) {
this.controller.value = '';
}
},
watch: {
message() {
this.reload();
},
},
computed: {
hasEmptyOperator() {
return (
!!this.controller.operator &&
this.controller.operator.indexOf('empty') > 0
);
},
methods: {
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
getCode() {
if (this.node && this.node.data.code && this.node.data.debug) {
let status = this.node.data.code;
return status.toLowerCase()[0].toUpperCase() + status.toLowerCase().substr(1);
}
return '';
},
remove() {
this.$emit('remove', this.controller, this.node);
},
copyRow() {
this.$emit('copyRow', this.controller, this.node);
},
change(value) {
if (value.indexOf("empty") > 0 && !!this.controller.value) {
this.controller.value = "";
}
},
},
computed: {
hasEmptyOperator() {
return !!this.controller.operator && this.controller.operator.indexOf("empty") > 0;
}
}
}
},
};
</script>
<style scoped>
.ms-btn {
width: 15%;
margin-left: 5px;
}
.ms-btn {
width: 15%;
margin-left: 5px;
}
.ms-select {
width: 15%;
margin-left: 5px;
}
.ms-select {
width: 15%;
margin-left: 5px;
}
.ms-req-error {
color: #F56C6C;
}
.ms-req-error {
color: #f56c6c;
}
.ms-req-success {
color: #67C23A;
}
.ms-step-debug-code {
display: inline-block;
margin: 0 5px;
overflow-x: hidden;
padding-bottom: 0;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
width: 80px;
}
.ms-test-running {
color: #783887;
}
.ms-req-success {
color: #67c23a;
}
.ms-step-debug-code {
display: inline-block;
margin: 0 5px;
overflow-x: hidden;
padding-bottom: 0;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
width: 80px;
}
.ms-test-running {
color: #783887;
}
</style>

View File

@ -10,29 +10,34 @@
:show-btn="showBtn"
:show-version="showVersion"
:background-color="backgroundColor"
:title="title" v-loading="loading">
:title="title"
v-loading="loading"
>
<template v-slot:request>
<jdbc-processor-content
:showScript="false"
:scenarioId="scenarioId"
:request="request"/>
:request="request"
/>
</template>
</api-base-component>
</template>
<script>
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
import MsInstructionsIcon from "metersphere-frontend/src/components/MsInstructionsIcon";
import MsDropdown from "@/business/commons/MsDropdown";
import ApiBaseComponent from "../common/ApiBaseComponent";
import JdbcProcessorContent from "@/business/automation/scenario/common/JDBCProcessorContent";
import MsCodeEdit from 'metersphere-frontend/src/components/MsCodeEdit';
import MsInstructionsIcon from 'metersphere-frontend/src/components/MsInstructionsIcon';
import MsDropdown from '@/business/commons/MsDropdown';
import ApiBaseComponent from '../common/ApiBaseComponent';
import JdbcProcessorContent from '@/business/automation/scenario/common/JDBCProcessorContent';
export default {
name: "MsJdbcProcessor",
name: 'MsJdbcProcessor',
components: {
JdbcProcessorContent,
ApiBaseComponent, MsDropdown, MsInstructionsIcon, MsCodeEdit
ApiBaseComponent,
MsDropdown,
MsInstructionsIcon,
MsCodeEdit,
},
props: {
scenarioId: String,
@ -54,8 +59,7 @@ export default {
},
isReadOnly: {
type: Boolean,
default:
false
default: false,
},
request: Object,
title: String,
@ -63,10 +67,9 @@ export default {
backgroundColor: String,
node: {},
},
created() {
},
created() {},
data() {
return {loading: false};
return { loading: false };
},
methods: {
remove() {
@ -85,7 +88,7 @@ export default {
this.request.active = !this.request.active;
this.reload();
},
}
},
};
</script>
@ -98,5 +101,4 @@ export default {
margin-left: 20px;
padding: 7px;
}
</style>

View File

@ -10,127 +10,143 @@
:show-btn="showBtn"
:show-version="showVersion"
:background-color="defBackgroundColor"
:title="request.elementType">
<div style="height: 300px;width: 100%">
<ms-code-edit mode="xml" :data.sync="request.jmeterElement" theme="eclipse" ref="codeEdit"/>
:title="request.elementType"
>
<div style="height: 300px; width: 100%">
<ms-code-edit
mode="xml"
:data.sync="request.jmeterElement"
theme="eclipse"
ref="codeEdit"
/>
</div>
<template v-slot:debugStepCode>
<span v-if="node.data.testing" class="ms-test-running">
<i class="el-icon-loading" style="font-size: 16px"/>
{{ $t('commons.testing') }}
</span>
<span class="ms-step-debug-code" :class="node.data.code ==='ERROR'?'ms-req-error':'ms-req-success'" v-if="!loading && !node.data.testing && node.data.debug">
<i class="el-icon-loading" style="font-size: 16px" />
{{ $t('commons.testing') }}
</span>
<span
class="ms-step-debug-code"
:class="node.data.code === 'ERROR' ? 'ms-req-error' : 'ms-req-success'"
v-if="!loading && !node.data.testing && node.data.debug"
>
{{ getCode() }}
</span>
</template>
</api-base-component>
</template>
<script>
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
import MsInstructionsIcon from "metersphere-frontend/src/components/MsInstructionsIcon";
import MsDropdown from "@/business/commons/MsDropdown";
import ApiBaseComponent from "../common/ApiBaseComponent";
import Jsr233ProcessorContent from "../common/Jsr233ProcessorContent";
import MsCodeEdit from 'metersphere-frontend/src/components/MsCodeEdit';
import MsInstructionsIcon from 'metersphere-frontend/src/components/MsInstructionsIcon';
import MsDropdown from '@/business/commons/MsDropdown';
import ApiBaseComponent from '../common/ApiBaseComponent';
import Jsr233ProcessorContent from '../common/Jsr233ProcessorContent';
export default {
name: "JmeterElementComponent",
components: {Jsr233ProcessorContent, ApiBaseComponent, MsDropdown, MsInstructionsIcon, MsCodeEdit},
props: {
draggable: {
type: Boolean,
default: false,
},
message: String,
isReadOnly: {
type: Boolean,
default:
false
},
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
showVersion: {
type: Boolean,
default: true,
},
request: {
type: Object,
},
defTitle: {type: String, default: "Jmeter组件"},
defColor: {type: String, default: "#606260"},
defBackgroundColor: {type: String, default: "#F4F4FF"},
node: {},
export default {
name: 'JmeterElementComponent',
components: {
Jsr233ProcessorContent,
ApiBaseComponent,
MsDropdown,
MsInstructionsIcon,
MsCodeEdit,
},
props: {
draggable: {
type: Boolean,
default: false,
},
data() {
return {
loading: false,
message: String,
isReadOnly: {
type: Boolean,
default: false,
},
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
showVersion: {
type: Boolean,
default: true,
},
request: {
type: Object,
},
defTitle: { type: String, default: 'Jmeter组件' },
defColor: { type: String, default: '#606260' },
defBackgroundColor: { type: String, default: '#F4F4FF' },
node: {},
},
data() {
return {
loading: false,
};
},
watch: {
message() {
this.reload();
},
},
methods: {
reload() {
this.loading = true;
this.$nextTick(() => {
this.loading = false;
});
},
getCode() {
if (this.node && this.node.data.code && this.node.data.debug) {
let status = this.node.data.code;
return (
status.toLowerCase()[0].toUpperCase() + status.toLowerCase().substr(1)
);
}
return '';
},
remove() {
this.$emit('remove', this.request, this.node);
},
copyRow() {
this.$emit('copyRow', this.request, this.node);
},
active() {
this.request.active = !this.request.active;
if (this.node) {
this.node.expanded = this.request.active;
}
},
watch: {
message() {
this.reload();
},
},
methods: {
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
getCode() {
if (this.node && this.node.data.code && this.node.data.debug) {
let status = this.node.data.code;
return status.toLowerCase()[0].toUpperCase() + status.toLowerCase().substr(1);
}
return '';
},
remove() {
this.$emit('remove', this.request, this.node);
},
copyRow() {
this.$emit('copyRow', this.request, this.node);
},
active() {
this.request.active = !this.request.active;
if (this.node) {
this.node.expanded = this.request.active;
}
},
}
}
},
};
</script>
<style scoped>
:deep(.el-divider) {
margin-bottom: 10px;
}
.ms-req-error {
color: #F56C6C;
}
margin-bottom: 10px;
}
.ms-req-error {
color: #f56c6c;
}
.ms-req-success {
color: #67C23A;
}
.ms-step-debug-code {
display: inline-block;
margin: 0 5px;
overflow-x: hidden;
padding-bottom: 0;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
width: 80px;
}
.ms-test-running {
color: #783887;
}
.ms-req-success {
color: #67c23a;
}
.ms-step-debug-code {
display: inline-block;
margin: 0 5px;
overflow-x: hidden;
padding-bottom: 0;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
width: 80px;
}
.ms-test-running {
color: #783887;
}
</style>

View File

@ -11,10 +11,15 @@
:show-version="showVersion"
:background-color="backgroundColor"
:if-from-variable-advance="ifFromVariableAdvance"
:title="title" v-loading="loading">
:title="title"
v-loading="loading"
>
<!--自定义脚本-->
<legend style="width: 100%;display:table-column" v-if="request && this.request.type === 'JSR223Processor'">
<p class="ms-tip">{{ $t('api_test.definition.request.req_param') }} </p>
<legend
style="width: 100%; display: table-column"
v-if="request && this.request.type === 'JSR223Processor'"
>
<p class="ms-tip">{{ $t('api_test.definition.request.req_param') }}</p>
<el-tabs v-model="activeName" class="request-tabs" @tab-click="tabClick">
<!-- 请求头-->
<el-tab-pane label="脚本内容" name="baseScript">
@ -23,13 +28,26 @@
:is-pre-processor="isPreProcessor"
:node="node"
:protocol="protocol"
:is-read-only="this.jsr223Processor.disabled"/>
:is-read-only="this.jsr223Processor.disabled"
/>
</el-tab-pane>
<!-- 脚本步骤/断言步骤 -->
<el-tab-pane :label="$t('api_test.definition.request.pre_operation')" name="preOperate">
<span class="item-tabs" effect="dark" placement="top-start" slot="label" :key="request.preSize">
<el-tab-pane
:label="$t('api_test.definition.request.pre_operation')"
name="preOperate"
>
<span
class="item-tabs"
effect="dark"
placement="top-start"
slot="label"
:key="request.preSize"
>
{{ $t('api_test.definition.request.pre_operation') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.preSize > 0">
<div
class="el-step__icon is-text ms-api-col ms-header"
v-if="request.preSize > 0"
>
<div class="el-step__icon-inner">{{ request.preSize }}</div>
</div>
</span>
@ -42,11 +60,24 @@
v-if="activeName === 'preOperate'"
/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.post_operation')" name="postOperate">
<span class="item-tabs" effect="dark" placement="top-start" slot="label">
<el-tab-pane
:label="$t('api_test.definition.request.post_operation')"
name="postOperate"
>
<span
class="item-tabs"
effect="dark"
placement="top-start"
slot="label"
>
{{ $t('api_test.definition.request.post_operation') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.postSize > 0">
<div class="el-step__icon-inner" :key="request.postSize">{{ request.postSize }}</div>
<div
class="el-step__icon is-text ms-api-col ms-header"
v-if="request.postSize > 0"
>
<div class="el-step__icon-inner" :key="request.postSize">
{{ request.postSize }}
</div>
</div>
</span>
<ms-jmx-step
@ -58,11 +89,24 @@
v-if="activeName === 'postOperate'"
/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.assertions_rule')" name="assertionsRule">
<span class="item-tabs" effect="dark" placement="top-start" slot="label">
<el-tab-pane
:label="$t('api_test.definition.request.assertions_rule')"
name="assertionsRule"
>
<span
class="item-tabs"
effect="dark"
placement="top-start"
slot="label"
>
{{ $t('api_test.definition.request.assertions_rule') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.ruleSize > 0">
<div class="el-step__icon-inner" :key="request.ruleSize">{{ request.ruleSize }}</div>
<div
class="el-step__icon is-text ms-api-col ms-header"
v-if="request.ruleSize > 0"
>
<div class="el-step__icon-inner" :key="request.ruleSize">
{{ request.ruleSize }}
</div>
</div>
</span>
<ms-jmx-step
@ -71,7 +115,8 @@
:response="response"
:tab-type="'assertionsRule'"
ref="assertionsRule"
v-if="activeName === 'assertionsRule'"/>
v-if="activeName === 'assertionsRule'"
/>
</el-tab-pane>
</el-tabs>
</legend>
@ -83,52 +128,95 @@
:is-pre-processor="isPreProcessor"
:node="node"
:protocol="protocol"
:is-read-only="this.jsr223Processor.disabled"/>
:is-read-only="this.jsr223Processor.disabled"
/>
</legend>
<template v-slot:debugStepCode>
<span v-if="jsr223Processor.testing" class="ms-test-running">
<i class="el-icon-loading" style="font-size: 16px"/>
{{ $t('commons.testing') }}
</span>
<span class="ms-step-debug-code" :class="jsr223Processor.requestResult[0].success && reqSuccess?'ms-req-success':'ms-req-error'"
v-if="!loading &&!jsr223Processor.testing && jsr223Processor.debug && jsr223Processor.requestResult[0] && jsr223Processor.requestResult[0].responseResult">
{{ jsr223Processor.requestResult[0].success && reqSuccess ? 'Success' : 'Error' }}
</span>
<span v-if="jsr223Processor.testing" class="ms-test-running">
<i class="el-icon-loading" style="font-size: 16px" />
{{ $t('commons.testing') }}
</span>
<span
class="ms-step-debug-code"
:class="
jsr223Processor.requestResult[0].success && reqSuccess
? 'ms-req-success'
: 'ms-req-error'
"
v-if="
!loading &&
!jsr223Processor.testing &&
jsr223Processor.debug &&
jsr223Processor.requestResult[0] &&
jsr223Processor.requestResult[0].responseResult
"
>
{{
jsr223Processor.requestResult[0].success && reqSuccess
? 'Success'
: 'Error'
}}
</span>
</template>
<!-- 执行结果内容 -->
<template v-slot:result>
<div v-loading="loading" v-if="jsr223Processor && jsr223Processor.requestResult && jsr223Processor.requestResult.length > 0">
<p class="tip">{{ $t('api_test.definition.request.res_param') }} </p>
<el-tabs v-model="jsr223Processor.activeName" closable class="ms-tabs" v-if="jsr223Processor.requestResult && jsr223Processor.requestResult.length > 1">
<el-tab-pane v-for="(item,i) in jsr223Processor.requestResult" :label="'循环'+(i+1)" :key="i" style="margin-bottom: 5px">
<div
v-loading="loading"
v-if="
jsr223Processor &&
jsr223Processor.requestResult &&
jsr223Processor.requestResult.length > 0
"
>
<p class="tip">{{ $t('api_test.definition.request.res_param') }}</p>
<el-tabs
v-model="jsr223Processor.activeName"
closable
class="ms-tabs"
v-if="
jsr223Processor.requestResult &&
jsr223Processor.requestResult.length > 1
"
>
<el-tab-pane
v-for="(item, i) in jsr223Processor.requestResult"
:label="'循环' + (i + 1)"
:key="i"
style="margin-bottom: 5px"
>
<api-response-component
:currentProtocol="jsr223Processor.protocol"
:apiActive="true"
:result="item"/>
:result="item"
/>
</el-tab-pane>
</el-tabs>
<api-response-component
:currentProtocol="'HTTP'"
:apiActive="true"
:result="jsr223Processor.requestResult[0]"
v-else/>
v-else
/>
</div>
</template>
</api-base-component>
</template>
<script>
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
import MsInstructionsIcon from "metersphere-frontend/src/components/MsInstructionsIcon";
import MsDropdown from "@/business/commons/MsDropdown";
import ApiBaseComponent from "../common/ApiBaseComponent";
import Jsr233ProcessorContent from "../common/Jsr233ProcessorContent";
import ApiResponseComponent from "./ApiResponseComponent";
import {stepCompute, hisDataProcessing} from "@/business/definition/api-definition";
import MsCodeEdit from 'metersphere-frontend/src/components/MsCodeEdit';
import MsInstructionsIcon from 'metersphere-frontend/src/components/MsInstructionsIcon';
import MsDropdown from '@/business/commons/MsDropdown';
import ApiBaseComponent from '../common/ApiBaseComponent';
import Jsr233ProcessorContent from '../common/Jsr233ProcessorContent';
import ApiResponseComponent from './ApiResponseComponent';
import {
stepCompute,
hisDataProcessing,
} from '@/business/definition/api-definition';
export default {
name: "MsJsr233Processor",
name: 'MsJsr233Processor',
components: {
Jsr233ProcessorContent,
ApiBaseComponent,
@ -136,7 +224,7 @@ export default {
MsInstructionsIcon,
MsCodeEdit,
ApiResponseComponent,
MsJmxStep: () => import( "@/business/definition/components/step/JmxStep")
MsJmxStep: () => import('@/business/definition/components/step/JmxStep'),
},
props: {
request: {},
@ -160,16 +248,14 @@ export default {
protocol: String,
isReadOnly: {
type: Boolean,
default:
false
default: false,
},
jsr223Processor: {
type: Object,
},
isPreProcessor: {
type: Boolean,
default:
false
default: false,
},
title: String,
color: String,
@ -181,7 +267,11 @@ export default {
},
},
created() {
if (this.request && this.request.type === 'JSR223Processor' && this.request.hashTree) {
if (
this.request &&
this.request.type === 'JSR223Processor' &&
this.request.hashTree
) {
this.initStepSize(this.request.hashTree);
this.historicalDataProcessing(this.request.hashTree);
}
@ -195,16 +285,16 @@ export default {
handler(v) {
this.initStepSize(this.request.hashTree);
},
deep: true
}
deep: true,
},
},
data() {
return {
loading: false,
reqSuccess: true,
response: {},
activeName: "baseScript"
}
activeName: 'baseScript',
};
},
methods: {
historicalDataProcessing(array) {
@ -234,26 +324,37 @@ export default {
}
},
forStatus() {
if (this.jsr223Processor && this.jsr223Processor.result && this.jsr223Processor.result.length > 0) {
this.jsr223Processor.result.forEach(item => {
item.requestResult.forEach(req => {
if (
this.jsr223Processor &&
this.jsr223Processor.result &&
this.jsr223Processor.result.length > 0
) {
this.jsr223Processor.result.forEach((item) => {
item.requestResult.forEach((req) => {
if (!req.success) {
this.reqSuccess = req.success;
}
})
})
} else if (this.jsr223Processor && this.jsr223Processor.requestResult && this.jsr223Processor.requestResult.length > 1) {
this.jsr223Processor.requestResult.forEach(item => {
});
});
} else if (
this.jsr223Processor &&
this.jsr223Processor.requestResult &&
this.jsr223Processor.requestResult.length > 1
) {
this.jsr223Processor.requestResult.forEach((item) => {
if (!item.success) {
this.reqSuccess = item.success;
if (this.node && this.node.parent && this.node.parent.data) {
this.node.parent.data.code = 'ERROR';
}
}
})
});
}
if (this.jsr223Processor.requestResult && this.jsr223Processor.requestResult.length > 0) {
this.response = this.jsr223Processor.requestResult[0]
if (
this.jsr223Processor.requestResult &&
this.jsr223Processor.requestResult.length > 0
) {
this.response = this.jsr223Processor.requestResult[0];
}
},
remove() {
@ -263,17 +364,17 @@ export default {
this.$emit('copyRow', this.jsr223Processor, this.node);
},
reload() {
this.loading = true
this.loading = true;
this.$nextTick(() => {
this.loading = false
})
this.loading = false;
});
},
active() {
this.jsr223Processor.active = !this.jsr223Processor.active;
this.reload();
},
}
}
},
};
</script>
<style scoped>
@ -282,7 +383,7 @@ export default {
}
.ms-req-error {
color: #F56C6C;
color: #f56c6c;
}
.ms-test-running {
@ -290,7 +391,7 @@ export default {
}
.ms-req-success {
color: #67C23A;
color: #67c23a;
}
.ms-header {

Some files were not shown because too many files have changed in this diff Show More