feat(系统设置): 自定义函数测试执行

This commit is contained in:
shiziyuan9527 2021-09-12 14:06:34 +08:00 committed by 刘瑞斌
parent 4ab568610b
commit 2d7d360ec4
5 changed files with 192 additions and 12 deletions

View File

@ -2,6 +2,7 @@ package io.metersphere.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.base.domain.CustomFunction;
import io.metersphere.base.domain.CustomFunctionWithBLOBs;
import io.metersphere.commons.utils.PageUtils;
@ -54,4 +55,9 @@ public class CustomFunctionController {
public CustomFunctionWithBLOBs get(@PathVariable String id) {
return customFunctionService.get(id);
}
@PostMapping("/run")
public String run(@RequestBody RunDefinitionRequest request) {
return customFunctionService.run(request);
}
}

View File

@ -1,15 +1,20 @@
package io.metersphere.service;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.domain.CustomFunction;
import io.metersphere.base.domain.CustomFunctionExample;
import io.metersphere.base.domain.CustomFunctionWithBLOBs;
import io.metersphere.base.mapper.CustomFunctionMapper;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.CustomFunctionRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.jorphan.collections.HashTree;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -28,6 +33,8 @@ public class CustomFunctionService {
@Resource
private CustomFunctionMapper customFunctionMapper;
@Resource
private JMeterService jMeterService;
public CustomFunctionWithBLOBs save(CustomFunctionRequest request) {
request.setId(UUID.randomUUID().toString());
@ -106,4 +113,13 @@ public class CustomFunctionService {
}
return customFunctionMapper.selectByPrimaryKey(id);
}
public String run(RunDefinitionRequest request) {
ParameterConfig config = new ParameterConfig();
config.setProjectId(request.getProjectId());
HashTree hashTree = request.getTestElement().generateHashTree(config);
String runMode = ApiRunMode.DEFINITION.name();
jMeterService.runLocal(request.getId(), hashTree, request.getReportId(), runMode);
return request.getId();
}
}

View File

@ -49,25 +49,34 @@
ref="codeEdit"/>
</el-tab-pane>
<el-tab-pane :label="'执行结果'" name="result">
执行结果
<div v-loading="runResult.loading">
<ms-code-edit :mode="'text'" :data.sync="console" v-if="isResultAlive" height="330px" ref="funcResult"/>
</div>
</el-tab-pane>
</el-tabs>
</template>
</el-form-item>
</el-col>
<el-col :span="4" class="script-index">
<ms-dropdown :default-command="form.type" :commands="languages" @command="languageChange"/>
<div class="template-title">{{ $t('api_test.request.processor.code_template') }}</div>
<div v-for="(template, index) in codeTemplates" :key="index" class="code-template">
<el-link :disabled="template.disabled" @click="addTemplate(template)">{{ template.title }}</el-link>
<div style="margin-top: -25px; margin-left: 10px;">
<div style="margin-bottom: 10px;">
<el-button type="primary" size="mini" style="width: 70px;" @click="handleTest" :disabled="runResult.loading">测试</el-button>
</div>
<ms-dropdown :default-command="form.type" :commands="languages" @command="languageChange"/>
<div class="template-title">{{ $t('api_test.request.processor.code_template') }}</div>
<div v-for="(template, index) in codeTemplates" :key="index" class="code-template">
<el-link :disabled="template.disabled" @click="addTemplate(template)">{{ template.title }}</el-link>
</div>
<el-link href="https://jmeter.apache.org/usermanual/component_reference.html#BeanShell_PostProcessor"
target="componentReferenceDoc" style="margin-top: 10px"
type="primary">{{ $t('commons.reference_documentation') }}
</el-link>
</div>
<el-link href="https://jmeter.apache.org/usermanual/component_reference.html#BeanShell_PostProcessor"
target="componentReferenceDoc" style="margin-top: 10px"
type="primary">{{ $t('commons.reference_documentation') }}
</el-link>
</el-col>
</el-row>
</el-form>
<!-- 执行组件 -->
<function-run :report-id="reportId" :run-data="runData" @runRefresh="runRefresh" @errorRefresh="errorRefresh"/>
</div>
<template v-slot:footer>
<el-button @click="close" size="medium">{{ $t('commons.cancel') }}</el-button>
@ -84,25 +93,38 @@ import FunctionParams from "@/business/components/settings/project/function/Func
import MsCodeEdit from "@/business/components/common/components/MsCodeEdit";
import MsDropdown from "@/business/components/common/components/MsDropdown";
import {splicingCustomFunc} from "@/business/components/settings/project/function/custom_function";
import MsRun from "@/business/components/api/automation/scenario/DebugRun";
import {getUUID} from "@/common/js/utils";
import {JSR223Processor} from "@/business/components/api/definition/model/ApiTestModel";
import FunctionRun from "@/business/components/settings/project/function/FunctionRun";
export default {
name: "EditFunction",
components: {
FunctionRun,
MsCodeEdit,
FunctionParams,
MsInputTag,
MsDropdown
MsDropdown,
MsRun
},
props: {},
data() {
return {
visible: false,
result: {},
runResult: {
loading: false
},
reportId: "",
runData: [],
isStop: false,
dialogCreateTitle: "创建函数",
dialogUpdateTitle: "更新函数",
activeName: 'code',
dialogTitle: "",
isCodeEditAlive: true,
isResultAlive: true,
isFormAlive: true,
form: {
params: [],
@ -179,6 +201,10 @@ export default {
disabled: this.isPreProcessor
}
],
response: {},
request: {},
debug: true,
console: "无执行结果"
}
},
watch: {
@ -204,6 +230,7 @@ export default {
params.join(",\s");
}
}
// todo
return params;
},
splicingFunc() {
@ -215,6 +242,7 @@ export default {
this.reloadCodeEdit();
},
open(data) {
this.activeName = "code";
this.visible = true;
this.form.type = "beanshell";
if (data && data.id) {
@ -248,6 +276,7 @@ export default {
type: "beanshell",
params: [{}]
};
this.console = "无执行结果";
this.visible = false;
},
languageChange(language) {
@ -274,6 +303,10 @@ export default {
this.isCodeEditAlive = false;
this.$nextTick(() => (this.isCodeEditAlive = true));
},
reloadResult() {
this.isResultAlive = false;
this.$nextTick(() => (this.isResultAlive = true));
},
submit() {
let param = Object.assign({}, this.form);
param.params = JSON.stringify(this.form.params);
@ -298,6 +331,29 @@ export default {
this.$emit("refresh");
this.$success(this.$t('commons.modify_success'));
})
},
handleTest() {
this.activeName = "result";
this.console = "无执行结果";
this.reloadResult();
this.runResult.loading = true;
let jSR223Processor = new JSR223Processor({
script: this.form.script
});
jSR223Processor.id = getUUID().substring(0, 8);
this.runData = [];
this.runData.push(jSR223Processor);
this.reportId = getUUID().substring(0, 8);
},
runRefresh(data) {
this.response = data;
this.console = this.response.responseResult.console;
this.runResult.loading = false;
this.reloadResult();
},
errorRefresh() {
this.runResult.loading = false;
}
}
}
@ -308,6 +364,7 @@ export default {
margin-bottom: 5px;
font-weight: bold;
font-size: 15px;
margin-top: 1px;
}
/* 滚动条样式 */

View File

@ -0,0 +1,101 @@
<template>
<span></span>
</template>
<script>
import {getCurrentProjectID, strMapToObj} from "@/common/js/utils";
import {TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
import TestPlan from "@/business/components/api/definition/components/jmeter/components/test-plan";
import ThreadGroup from "@/business/components/api/definition/components/jmeter/components/thread-group";
export default {
name: 'FunctionRun',
components: {},
props: {
environment: Object,
debug: Boolean,
reportId: String,
runData: Array,
type: String,
envMap: Map,
isStop: Boolean,
},
data() {
return {
result: {},
loading: false,
runId: "",
reqNumber: 0,
websocket: {}
}
},
watch: {
//
reportId() {
this.run()
},
isStop() {
if (!this.isStop && this.websocket && this.websocket.close instanceof Function) {
this.websocket.close();
}
}
},
methods: {
initWebSocket() {
let protocol = "ws://";
if (window.location.protocol === 'https:') {
protocol = "wss://";
}
const uri = protocol + window.location.host + "/api/definition/run/report/" + this.runId + "/debug";
this.websocket = new WebSocket(uri);
this.websocket.onmessage = this.onMessage;
},
onMessage(e) {
if (e.data) {
let data = JSON.parse(e.data);
this.websocket.close();
this.$emit('runRefresh', data);
}
},
sort(stepArray) {
if (stepArray) {
for (let i in stepArray) {
if (!stepArray[i].clazzName) {
stepArray[i].clazzName = TYPE_TO_C.get(stepArray[i].type);
}
if (stepArray[i].hashTree && stepArray[i].hashTree.length > 0) {
this.sort(stepArray[i].hashTree);
}
}
}
},
run() {
let projectId = getCurrentProjectID();
let testPlan = new TestPlan();
testPlan.clazzName = TYPE_TO_C.get(testPlan.type);
let threadGroup = new ThreadGroup();
threadGroup.clazzName = TYPE_TO_C.get(threadGroup.type);
threadGroup.hashTree = [];
testPlan.hashTree = [threadGroup];
this.runData.forEach(item => {
item.projectId = projectId;
if (!item.clazzName) {
item.clazzName = TYPE_TO_C.get(item.type);
}
threadGroup.hashTree.push(item);
})
this.sort(testPlan.hashTree);
let reqObj = {id: this.reportId, testElement: testPlan, type: this.type, clazzName: this.clazzName ? this.clazzName : TYPE_TO_C.get(this.type), projectId: projectId, environmentMap: strMapToObj(this.envMap)};
let url = "/custom/func/run";
reqObj.reportId = this.reportId;
this.$post(url, reqObj, res => {
this.runId = res.data;
this.initWebSocket();
}, () => {
this.$emit('errorRefresh', {});
})
}
}
}
</script>

View File

@ -11,7 +11,7 @@ export function generateFuncFirstLine(funcLanguage, funcName, funcParams) {
let funcFirstLine = "";
switch (funcLanguage) {
case "beanshell":
funcFirstLine = "function " + funcName + "(" + funcParams + ") " + "{";
funcFirstLine = "public static void " + funcName + "(" + funcParams + ") " + "{";
break;
case "python":
break;
@ -27,7 +27,7 @@ export function generateFuncFirstLine(funcLanguage, funcName, funcParams) {
}
const regex = {
beanshell: /^function\s.*\(.*\)\s\{/,
beanshell: /^public static void\s.*\(.*\)\s\{/,
python: /^function\s.*\(.*\)\s\{/,
groovy: /^function\s.*\(.*\)\s\{/,
nashornScript: /^function\s.*\(.*\)\s\{/,