Merge branch 'dev' of https://github.com/fit2cloudrd/metersphere-server into dev
This commit is contained in:
commit
df7f4c18da
|
@ -20,92 +20,88 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
*/
|
||||
public class APIBackendListenerClient extends AbstractBackendListenerClient implements Serializable {
|
||||
|
||||
// 与前端JMXGenerator的SPLIT对应,用于获取 测试名称 和 测试ID
|
||||
private final static String SPLIT = "@@:";
|
||||
// 测试ID作为key
|
||||
private final Map<String, List<SampleResult>> queue = new ConcurrentHashMap<>();
|
||||
private final static String THREAD_SPLIT = " ";
|
||||
|
||||
private final static String ID_SPLIT = "-";
|
||||
|
||||
private final static String TEST_ID = "id";
|
||||
|
||||
private final List<SampleResult> queue = new ArrayList<>();
|
||||
|
||||
private APITestService apiTestService;
|
||||
|
||||
private APIReportService apiReportService;
|
||||
|
||||
// 测试ID
|
||||
private String id;
|
||||
|
||||
@Override
|
||||
public void setupTest(BackendListenerContext context) throws Exception {
|
||||
this.id = context.getParameter(TEST_ID);
|
||||
apiTestService = CommonBeanFactory.getBean(APITestService.class);
|
||||
if (apiTestService == null) {
|
||||
LogUtil.error("apiTestService is required");
|
||||
}
|
||||
|
||||
apiReportService = CommonBeanFactory.getBean(APIReportService.class);
|
||||
if (apiReportService == null) {
|
||||
LogUtil.error("apiReportService is required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSampleResults(List<SampleResult> sampleResults, BackendListenerContext context) {
|
||||
sampleResults.forEach(result -> {
|
||||
// 将不同的测试脚本按测试ID分开
|
||||
String label = result.getSampleLabel();
|
||||
if (!label.contains(SPLIT)) {
|
||||
LogUtil.error("request name format is invalid, name: " + label);
|
||||
return;
|
||||
}
|
||||
String name = label.split(SPLIT)[0];
|
||||
String testId = label.split(SPLIT)[1];
|
||||
if (!queue.containsKey(testId)) {
|
||||
List<SampleResult> testResults = new ArrayList<>();
|
||||
queue.put(testId, testResults);
|
||||
}
|
||||
result.setSampleLabel(name);
|
||||
queue.get(testId).add(result);
|
||||
});
|
||||
queue.addAll(sampleResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teardownTest(BackendListenerContext context) throws Exception {
|
||||
APITestService apiTestService = CommonBeanFactory.getBean(APITestService.class);
|
||||
if (apiTestService == null) {
|
||||
LogUtil.error("apiTestService is required");
|
||||
return;
|
||||
}
|
||||
TestResult testResult = new TestResult();
|
||||
testResult.setTestId(id);
|
||||
testResult.setTotal(queue.size());
|
||||
|
||||
APIReportService apiReportService = CommonBeanFactory.getBean(APIReportService.class);
|
||||
if (apiReportService == null) {
|
||||
LogUtil.error("apiReportService is required");
|
||||
return;
|
||||
}
|
||||
// 一个脚本里可能包含多个场景(ThreadGroup),所以要区分开,key: 场景Id
|
||||
final Map<String, ScenarioResult> scenarios = new LinkedHashMap<>();
|
||||
|
||||
queue.forEach((id, sampleResults) -> {
|
||||
TestResult testResult = new TestResult();
|
||||
testResult.setTestId(id);
|
||||
testResult.setTotal(sampleResults.size());
|
||||
queue.forEach(result -> {
|
||||
// 线程名称: <场景名> <场景Index>-<请求Index>, 例如:Scenario 2-1
|
||||
String scenarioName = StringUtils.substringBeforeLast(result.getThreadName(), THREAD_SPLIT);
|
||||
String index = StringUtils.substringAfterLast(result.getThreadName(), THREAD_SPLIT);
|
||||
String scenarioId = StringUtils.substringBefore(index, ID_SPLIT);
|
||||
ScenarioResult scenarioResult;
|
||||
if (!scenarios.containsKey(scenarioId)) {
|
||||
scenarioResult = new ScenarioResult();
|
||||
scenarioResult.setId(scenarioId);
|
||||
scenarioResult.setName(scenarioName);
|
||||
scenarios.put(scenarioId, scenarioResult);
|
||||
} else {
|
||||
scenarioResult = scenarios.get(scenarioId);
|
||||
}
|
||||
|
||||
// key: 场景Id
|
||||
final Map<String, ScenarioResult> scenarios = new LinkedHashMap<>();
|
||||
if (result.isSuccessful()) {
|
||||
scenarioResult.addSuccess();
|
||||
testResult.addSuccess();
|
||||
} else {
|
||||
scenarioResult.addError(result.getErrorCount());
|
||||
testResult.addError(result.getErrorCount());
|
||||
}
|
||||
|
||||
sampleResults.forEach(result -> {
|
||||
String thread = StringUtils.substringBeforeLast(result.getThreadName(), " ");
|
||||
String order = StringUtils.substringAfterLast(result.getThreadName(), " ");
|
||||
String scenarioName = StringUtils.substringBefore(thread, SPLIT);
|
||||
String scenarioId = StringUtils.substringAfter(thread, SPLIT);
|
||||
ScenarioResult scenarioResult;
|
||||
if (!scenarios.containsKey(scenarioId)) {
|
||||
scenarioResult = new ScenarioResult();
|
||||
scenarioResult.setId(scenarioId);
|
||||
scenarioResult.setName(scenarioName);
|
||||
scenarioResult.setOrder(StringUtils.substringBefore(order, "-"));
|
||||
scenarios.put(scenarioId, scenarioResult);
|
||||
} else {
|
||||
scenarioResult = scenarios.get(scenarioId);
|
||||
}
|
||||
RequestResult requestResult = getRequestResult(result);
|
||||
scenarioResult.getRequestResults().add(requestResult);
|
||||
scenarioResult.addResponseTime(result.getTime());
|
||||
|
||||
if (result.isSuccessful()) {
|
||||
scenarioResult.addSuccess();
|
||||
testResult.addSuccess();
|
||||
} else {
|
||||
scenarioResult.addError(result.getErrorCount());
|
||||
testResult.addError(result.getErrorCount());
|
||||
}
|
||||
testResult.addPassAssertions(requestResult.getPassAssertions());
|
||||
testResult.addTotalAssertions(requestResult.getTotalAssertions());
|
||||
|
||||
RequestResult requestResult = getRequestResult(result);
|
||||
scenarioResult.getRequestResults().add(requestResult);
|
||||
scenarioResult.addResponseTime(result.getTime());
|
||||
|
||||
testResult.addPassAssertions(requestResult.getPassAssertions());
|
||||
testResult.addTotalAssertions(requestResult.getTotalAssertions());
|
||||
|
||||
scenarioResult.addPassAssertions(requestResult.getPassAssertions());
|
||||
scenarioResult.addTotalAssertions(requestResult.getTotalAssertions());
|
||||
});
|
||||
testResult.getScenarios().addAll(scenarios.values());
|
||||
testResult.getScenarios().sort(Comparator.comparing(ScenarioResult::getOrder));
|
||||
apiTestService.changeStatus(id, APITestStatus.Completed);
|
||||
apiReportService.complete(testResult);
|
||||
scenarioResult.addPassAssertions(requestResult.getPassAssertions());
|
||||
scenarioResult.addTotalAssertions(requestResult.getTotalAssertions());
|
||||
});
|
||||
|
||||
testResult.getScenarios().addAll(scenarios.values());
|
||||
testResult.getScenarios().sort(Comparator.comparing(ScenarioResult::getId));
|
||||
apiTestService.changeStatus(id, APITestStatus.Completed);
|
||||
apiReportService.complete(testResult);
|
||||
|
||||
queue.clear();
|
||||
super.teardownTest(context);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ public class ScenarioResult {
|
|||
|
||||
private String name;
|
||||
|
||||
private String order;
|
||||
|
||||
private long responseTime;
|
||||
|
||||
private int error = 0;
|
||||
|
|
|
@ -45,11 +45,11 @@ public class UserRoleService {
|
|||
for (int i = 0; i < collect.size(); i++) {
|
||||
Map<String, Object> map = new HashMap<>(2);
|
||||
map.put("id", collect.get(i));
|
||||
map.put("Ids", new ArrayList<>());
|
||||
map.put("ids", new ArrayList<>());
|
||||
for (int j = 0; j < userRoles.size(); j++) {
|
||||
String role = userRoles.get(j).getRoleId();
|
||||
if (StringUtils.equals(role, collect.get(i))) {
|
||||
List ids = (List) map.get("Ids");
|
||||
List ids = (List) map.get("ids");
|
||||
ids.add(userRoles.get(j).getSourceId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,10 +80,10 @@ public class UserService {
|
|||
userRole.setSourceId("adminSourceId");
|
||||
userRoleMapper.insertSelective(userRole);
|
||||
} else {
|
||||
// if (!map.keySet().contains("Ids")) {
|
||||
// if (!map.keySet().contains("ids")) {
|
||||
// MSException.throwException(role + " no source id");
|
||||
// }
|
||||
List<String> list = (List<String>) map.get("Ids");
|
||||
List<String> list = (List<String>) map.get("ids");
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
UserRole userRole1 = new UserRole();
|
||||
userRole1.setId(UUID.randomUUID().toString());
|
||||
|
|
|
@ -1,38 +1,26 @@
|
|||
<template>
|
||||
<div class="container" v-loading="result.loading">
|
||||
<div class="container" v-loading="loading">
|
||||
<div class="main-content">
|
||||
<el-card>
|
||||
<section class="report-container" v-if="this.report.testId">
|
||||
<header class="report-header">
|
||||
<span>{{report.projectName}} / </span>
|
||||
<router-link :to="path">{{report.testName}} [{{report.createTime | timestampFormatDate}}]</router-link>
|
||||
<router-link :to="path">{{report.testName}}</router-link>
|
||||
<span class="time">{{report.createTime | timestampFormatDate}}</span>
|
||||
</header>
|
||||
<main v-if="this.isCompleted">
|
||||
<div class="scenario-chart">
|
||||
<ms-metric-chart :content="content"></ms-metric-chart>
|
||||
</div>
|
||||
<el-card>
|
||||
<div class="scenario-header">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="16">
|
||||
{{$t('api_report.scenario_name')}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{$t('api_report.response_time')}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{$t('api_report.error')}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{$t('api_report.assertions')}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{$t('api_report.result')}}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<ms-scenario-result v-for="(scenario, index) in content.scenarios" :key="index" :scenario="scenario"/>
|
||||
</el-card>
|
||||
<main v-if="this.isNotRunning">
|
||||
<ms-metric-chart :content="content"/>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="$t('api_report.total')" name="total">
|
||||
<ms-scenario-results :scenarios="content.scenarios"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="fail">
|
||||
<template slot="label">
|
||||
<span class="fail">{{$t('api_report.fail')}}</span>
|
||||
</template>
|
||||
<ms-scenario-results :scenarios="fails"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</main>
|
||||
</section>
|
||||
</el-card>
|
||||
|
@ -45,33 +33,63 @@
|
|||
import MsRequestResult from "./components/RequestResult";
|
||||
import MsScenarioResult from "./components/ScenarioResult";
|
||||
import MsMetricChart from "./components/MetricChart";
|
||||
import MsScenarioResults from "./components/ScenarioResults";
|
||||
|
||||
export default {
|
||||
name: "MsApiReportView",
|
||||
components: {MsMetricChart, MsScenarioResult, MsRequestResult},
|
||||
components: {MsScenarioResults, MsMetricChart, MsScenarioResult, MsRequestResult},
|
||||
data() {
|
||||
return {
|
||||
activeName: "total",
|
||||
content: {},
|
||||
report: {},
|
||||
result: {},
|
||||
loading: true,
|
||||
fails: []
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getReport() {
|
||||
init() {
|
||||
this.loading = true;
|
||||
this.report = {};
|
||||
this.content = {};
|
||||
this.fails = [];
|
||||
},
|
||||
getReport() {
|
||||
this.init();
|
||||
|
||||
if (this.reportId) {
|
||||
let url = "/api/report/get/" + this.reportId;
|
||||
this.result = this.$get(url, response => {
|
||||
this.$get(url, response => {
|
||||
this.report = response.data || {};
|
||||
if (this.isCompleted) {
|
||||
if (this.isNotRunning) {
|
||||
this.content = JSON.parse(this.report.content);
|
||||
this.getFails();
|
||||
this.loading = false;
|
||||
} else {
|
||||
setTimeout(this.getReport, 2000)
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
getFails() {
|
||||
if (this.isNotRunning) {
|
||||
this.fails = [];
|
||||
this.content.scenarios.forEach((scenario) => {
|
||||
let failScenario = Object.assign({}, scenario);
|
||||
if (scenario.error > 0) {
|
||||
this.fails.push(failScenario);
|
||||
failScenario.requestResults = [];
|
||||
scenario.requestResults.forEach((request) => {
|
||||
if (!request.success) {
|
||||
let failRequest = Object.assign({}, request);
|
||||
failScenario.requestResults.push(failRequest);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -90,12 +108,17 @@
|
|||
path() {
|
||||
return "/api/test/edit?id=" + this.report.testId;
|
||||
},
|
||||
isCompleted() {
|
||||
return "Completed" === this.report.status;
|
||||
isNotRunning() {
|
||||
return "Running" !== this.report.status;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.report-container .el-tabs__header {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.report-container {
|
||||
|
@ -112,11 +135,16 @@
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.scenario-header {
|
||||
border: 1px solid #EBEEF5;
|
||||
background-color: #F9FCFF;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
padding: 5px 0;
|
||||
.report-header .time {
|
||||
color: #909399;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.report-container .fail {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
.report-container .is-active .fail {
|
||||
color: inherit;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<template>
|
||||
<el-table :data="assertions" :row-style="getRowStyle" :header-cell-style="getRowStyle">
|
||||
<el-table-column prop="name" :label="$t('api_report.assertions_name')" width="300">
|
||||
</el-table-column>
|
||||
<el-table-column prop="message" :label="$t('api_report.assertions_error_message')">
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" :label="$t('api_report.assertions_name')" width="300"/>
|
||||
<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">
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div>
|
||||
LogDetails
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "LogDetails",
|
||||
data() {
|
||||
return {
|
||||
seleniumLog: '',
|
||||
browserDriverLog: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,20 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
ResultDetails
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ResultDetails"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
#video {
|
||||
height: 50%;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<el-card>
|
||||
<div class="scenario-header">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="16">
|
||||
{{$t('api_report.scenario_name')}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{$t('api_report.response_time')}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{$t('api_report.error')}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{$t('api_report.assertions')}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{$t('api_report.result')}}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<ms-scenario-result v-for="(scenario, index) in scenarios" :key="index" :scenario="scenario"/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsScenarioResult from "./ScenarioResult";
|
||||
|
||||
export default {
|
||||
name: "MsScenarioResults",
|
||||
|
||||
components: {MsScenarioResult},
|
||||
|
||||
props: {
|
||||
scenarios: Array
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.scenario-header {
|
||||
border: 1px solid #EBEEF5;
|
||||
background-color: #F9FCFF;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
padding: 5px 0;
|
||||
}
|
||||
</style>
|
|
@ -114,9 +114,11 @@
|
|||
saveTest: function () {
|
||||
this.save(() => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$router.push({
|
||||
path: '/api/test/edit?id=' + this.test.id
|
||||
})
|
||||
if (this.create) {
|
||||
this.$router.push({
|
||||
path: '/api/test/edit?id=' + this.test.id
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
runTest: function () {
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
},
|
||||
copyRequest: function (index) {
|
||||
let request = this.requests[index];
|
||||
this.requests.push(request.clone());
|
||||
this.requests.push(new Request(request));
|
||||
},
|
||||
deleteRequest: function (index) {
|
||||
this.requests.splice(index, 1);
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
},
|
||||
copyScenario: function (index) {
|
||||
let scenario = this.scenarios[index];
|
||||
this.scenarios.push(scenario.clone());
|
||||
this.scenarios.push(new Scenario(scenario));
|
||||
},
|
||||
deleteScenario: function (index) {
|
||||
this.scenarios.splice(index, 1);
|
||||
|
|
|
@ -163,7 +163,7 @@ export class DefaultTestElement extends TestElement {
|
|||
super(tag, {
|
||||
guiclass: guiclass,
|
||||
testclass: testclass,
|
||||
testname: testname || tag + ' Name',
|
||||
testname: testname === undefined ? tag + ' Name' : testname,
|
||||
enabled: enabled || true
|
||||
});
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ export class DefaultTestElement extends TestElement {
|
|||
|
||||
export class TestPlan extends DefaultTestElement {
|
||||
constructor(testName, props) {
|
||||
super('TestPlan', 'TestPlanGui', 'TestPlan', testName || 'TestPlan');
|
||||
super('TestPlan', 'TestPlanGui', 'TestPlan', testName);
|
||||
|
||||
props = props || {};
|
||||
this.boolProp("TestPlan.functional_mode", props.mode || false);
|
||||
|
@ -212,7 +212,7 @@ export class ThreadGroup extends DefaultTestElement {
|
|||
|
||||
export class PostThreadGroup extends DefaultTestElement {
|
||||
constructor(testName, props) {
|
||||
super('PostThreadGroup', 'PostThreadGroupGui', 'PostThreadGroup', testName || 'tearDown Thread Group');
|
||||
super('PostThreadGroup', 'PostThreadGroupGui', 'PostThreadGroup', testName);
|
||||
|
||||
props = props || {};
|
||||
this.intProp("ThreadGroup.num_threads", props.threads || 1);
|
||||
|
@ -238,7 +238,7 @@ export class PostThreadGroup extends DefaultTestElement {
|
|||
|
||||
export class DebugSampler extends DefaultTestElement {
|
||||
constructor(testName) {
|
||||
super('DebugSampler', 'TestBeanGUI', 'DebugSampler', testName || 'Debug Sampler');
|
||||
super('DebugSampler', 'TestBeanGUI', 'DebugSampler', testName);
|
||||
|
||||
this.boolProp("displayJMeterProperties", false);
|
||||
this.boolProp("displayJMeterVariables", true);
|
||||
|
@ -248,7 +248,7 @@ export class DebugSampler extends DefaultTestElement {
|
|||
|
||||
export class HTTPSamplerProxy extends DefaultTestElement {
|
||||
constructor(testName, request) {
|
||||
super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName || 'HTTP Request');
|
||||
super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName);
|
||||
this.request = request || {};
|
||||
|
||||
this.stringProp("HTTPSampler.domain", this.request.hostname);
|
||||
|
@ -292,7 +292,7 @@ export class HTTPSamplerArguments extends Element {
|
|||
|
||||
export class DurationAssertion extends DefaultTestElement {
|
||||
constructor(testName, duration) {
|
||||
super('DurationAssertion', 'DurationAssertionGui', 'DurationAssertion', testName || 'Duration Assertion');
|
||||
super('DurationAssertion', 'DurationAssertionGui', 'DurationAssertion', testName);
|
||||
this.duration = duration || 0;
|
||||
this.stringProp('DurationAssertion.duration', this.duration);
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ export class DurationAssertion extends DefaultTestElement {
|
|||
|
||||
export class ResponseAssertion extends DefaultTestElement {
|
||||
constructor(testName, assertion) {
|
||||
super('ResponseAssertion', 'AssertionGui', 'ResponseAssertion', testName || 'Response Assertion');
|
||||
super('ResponseAssertion', 'AssertionGui', 'ResponseAssertion', testName);
|
||||
this.assertion = assertion || {};
|
||||
|
||||
this.stringProp('Assertion.test_field', this.assertion.field);
|
||||
|
@ -352,7 +352,7 @@ export class ResponseHeadersAssertion extends ResponseAssertion {
|
|||
|
||||
export class HeaderManager extends DefaultTestElement {
|
||||
constructor(testName, headers) {
|
||||
super('HeaderManager', 'HeaderPanel', 'HeaderManager', testName || 'HTTP Header manager');
|
||||
super('HeaderManager', 'HeaderPanel', 'HeaderManager', testName);
|
||||
this.headers = headers || [];
|
||||
|
||||
let collectionProp = this.collectionProp('HeaderManager.headers');
|
||||
|
@ -366,7 +366,7 @@ export class HeaderManager extends DefaultTestElement {
|
|||
|
||||
export class Arguments extends DefaultTestElement {
|
||||
constructor(testName, args) {
|
||||
super('Arguments', 'ArgumentsPanel', 'Arguments', testName || 'User Defined Variables');
|
||||
super('Arguments', 'ArgumentsPanel', 'Arguments', testName);
|
||||
this.args = args || [];
|
||||
|
||||
let collectionProp = this.collectionProp('Arguments.arguments');
|
||||
|
@ -382,7 +382,7 @@ export class Arguments extends DefaultTestElement {
|
|||
|
||||
export class BackendListener extends DefaultTestElement {
|
||||
constructor(testName, className, args) {
|
||||
super('BackendListener', 'BackendListenerGui', 'BackendListener', testName || 'Backend Listener');
|
||||
super('BackendListener', 'BackendListenerGui', 'BackendListener', testName);
|
||||
this.stringProp('classname', className);
|
||||
if (args && args.length > 0) {
|
||||
this.add(new ElementArguments(args));
|
||||
|
@ -415,7 +415,7 @@ export class ElementArguments extends Element {
|
|||
|
||||
export class RegexExtractor extends DefaultTestElement {
|
||||
constructor(testName, props) {
|
||||
super('RegexExtractor', 'RegexExtractorGui', 'RegexExtractor', testName || 'Regular Expression Extractor');
|
||||
super('RegexExtractor', 'RegexExtractorGui', 'RegexExtractor', testName);
|
||||
this.props = props || {}
|
||||
this.stringProp('RegexExtractor.useHeaders', props.headers);
|
||||
this.stringProp('RegexExtractor.refname', props.name);
|
||||
|
@ -428,7 +428,7 @@ export class RegexExtractor extends DefaultTestElement {
|
|||
|
||||
export class JSONPostProcessor extends DefaultTestElement {
|
||||
constructor(testName, props) {
|
||||
super('JSONPostProcessor', 'JSONPostProcessorGui', 'JSONPostProcessor', testName || 'JSON Extractor');
|
||||
super('JSONPostProcessor', 'JSONPostProcessorGui', 'JSONPostProcessor', testName);
|
||||
this.props = props || {}
|
||||
this.stringProp('JSONPostProcessor.referenceNames', props.name);
|
||||
this.stringProp('JSONPostProcessor.jsonPathExprs', props.expression);
|
||||
|
@ -438,7 +438,7 @@ export class JSONPostProcessor extends DefaultTestElement {
|
|||
|
||||
export class XPath2Extractor extends DefaultTestElement {
|
||||
constructor(testName, props) {
|
||||
super('XPath2Extractor', 'XPath2ExtractorGui', 'XPath2Extractor', testName || 'XPath2 Extractor');
|
||||
super('XPath2Extractor', 'XPath2ExtractorGui', 'XPath2Extractor', testName);
|
||||
this.props = props || {}
|
||||
this.stringProp('XPathExtractor2.default', props.default);
|
||||
this.stringProp('XPathExtractor2.refname', props.name);
|
||||
|
|
|
@ -5,8 +5,6 @@ import {
|
|||
TestElement,
|
||||
TestPlan,
|
||||
ThreadGroup,
|
||||
PostThreadGroup,
|
||||
DebugSampler,
|
||||
HeaderManager,
|
||||
HTTPSamplerArguments,
|
||||
ResponseCodeAssertion,
|
||||
|
@ -121,7 +119,6 @@ export class Test extends BaseConfig {
|
|||
export class Scenario extends BaseConfig {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.id = uuid();
|
||||
this.name = undefined;
|
||||
this.url = undefined;
|
||||
this.variables = [];
|
||||
|
@ -137,22 +134,11 @@ export class Scenario extends BaseConfig {
|
|||
options.requests = options.requests || [new Request()];
|
||||
return options;
|
||||
}
|
||||
|
||||
clone() {
|
||||
let scenario = new Scenario(this);
|
||||
scenario.id = uuid();
|
||||
scenario.requests.forEach(function (request) {
|
||||
request.id = uuid();
|
||||
});
|
||||
|
||||
return scenario;
|
||||
}
|
||||
}
|
||||
|
||||
export class Request extends BaseConfig {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.id = uuid();
|
||||
this.name = undefined;
|
||||
this.url = undefined;
|
||||
this.method = undefined;
|
||||
|
@ -178,12 +164,6 @@ export class Request extends BaseConfig {
|
|||
isValid() {
|
||||
return !!this.url && !!this.method
|
||||
}
|
||||
|
||||
clone() {
|
||||
let request = new Request(this);
|
||||
request.id = uuid();
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
export class Body extends BaseConfig {
|
||||
|
@ -402,17 +382,19 @@ class JMeterTestPlan extends Element {
|
|||
|
||||
class JMXGenerator {
|
||||
constructor(test) {
|
||||
if (!test || !(test instanceof Test)) return undefined;
|
||||
|
||||
if (!test.id) {
|
||||
test.id = "#NULL_TEST_ID#";
|
||||
}
|
||||
const SPLIT = "@@:";
|
||||
if (!test || !test.id || !(test instanceof Test)) return undefined;
|
||||
|
||||
let testPlan = new TestPlan(test.name);
|
||||
test.scenarioDefinition.forEach(scenario => {
|
||||
let testName = scenario.name ? scenario.name + SPLIT + scenario.id : SPLIT + scenario.id;
|
||||
let threadGroup = new ThreadGroup(testName);
|
||||
this.addScenarios(testPlan, test.scenarioDefinition);
|
||||
this.addBackendListener(testPlan, test.id);
|
||||
|
||||
this.jmeterTestPlan = new JMeterTestPlan();
|
||||
this.jmeterTestPlan.put(testPlan);
|
||||
}
|
||||
|
||||
addScenarios(testPlan, scenarios) {
|
||||
scenarios.forEach(scenario => {
|
||||
let threadGroup = new ThreadGroup(scenario.name || "");
|
||||
|
||||
this.addScenarioVariables(threadGroup, scenario);
|
||||
|
||||
|
@ -421,9 +403,7 @@ class JMXGenerator {
|
|||
scenario.requests.forEach(request => {
|
||||
if (!request.isValid()) return;
|
||||
|
||||
// test.id用于处理结果时区分属于哪个测试
|
||||
let name = request.name ? request.name + SPLIT + test.id : SPLIT + test.id;
|
||||
let httpSamplerProxy = new HTTPSamplerProxy(name, new JMXRequest(request));
|
||||
let httpSamplerProxy = new HTTPSamplerProxy(request.name || "", new JMXRequest(request));
|
||||
|
||||
this.addRequestHeader(httpSamplerProxy, request);
|
||||
|
||||
|
@ -440,19 +420,14 @@ class JMXGenerator {
|
|||
threadGroup.put(httpSamplerProxy);
|
||||
})
|
||||
|
||||
this.addBackendListener(threadGroup);
|
||||
testPlan.put(threadGroup);
|
||||
|
||||
// 暂时不加
|
||||
// let tearDownThreadGroup = new PostThreadGroup();
|
||||
// tearDownThreadGroup.put(new DebugSampler(test.id));
|
||||
// this.addBackendListener(tearDownThreadGroup);
|
||||
//
|
||||
// testPlan.put(tearDownThreadGroup);
|
||||
})
|
||||
}
|
||||
|
||||
this.jmeterTestPlan = new JMeterTestPlan();
|
||||
this.jmeterTestPlan.put(testPlan);
|
||||
addBackendListener(testPlan, testId) {
|
||||
let className = 'io.metersphere.api.jmeter.APIBackendListenerClient';
|
||||
let args = [new KeyValue("id", testId)];
|
||||
testPlan.put(new BackendListener(testId, className, args));
|
||||
}
|
||||
|
||||
addScenarioVariables(threadGroup, scenario) {
|
||||
|
@ -567,12 +542,6 @@ class JMXGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
addBackendListener(threadGroup) {
|
||||
let testName = 'API Backend Listener';
|
||||
let className = 'io.metersphere.api.jmeter.APIBackendListenerClient';
|
||||
threadGroup.put(new BackendListener(testName, className));
|
||||
}
|
||||
|
||||
filter(config) {
|
||||
return config.isValid();
|
||||
}
|
||||
|
|
|
@ -87,10 +87,10 @@
|
|||
</el-form-item>
|
||||
<div v-if="role.id === 'org_admin'">
|
||||
<el-form-item label="选择组织"
|
||||
:prop="'roles.' + index + '.Ids'"
|
||||
:prop="'roles.' + index + '.ids'"
|
||||
:rules="{required: true, message: '请选择组织', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.Ids" placeholder="选择组织" multiple>
|
||||
<el-select v-model="role.ids" placeholder="选择组织" multiple>
|
||||
<el-option
|
||||
v-for="item in form.orgList"
|
||||
:key="item.id"
|
||||
|
@ -102,10 +102,10 @@
|
|||
</div>
|
||||
<div v-if="role.id === 'test_manager'">
|
||||
<el-form-item label="选择工作空间"
|
||||
:prop="'roles.' + index + '.Ids'"
|
||||
:prop="'roles.' + index + '.ids'"
|
||||
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
|
||||
<el-select v-model="role.ids" placeholder="选择工作空间" multiple>
|
||||
<el-option
|
||||
v-for="item in form.wsList"
|
||||
:key="item.id"
|
||||
|
@ -117,10 +117,10 @@
|
|||
</div>
|
||||
<div v-if="role.id ==='test_user'">
|
||||
<el-form-item label="选择工作空间"
|
||||
:prop="'roles.' + index + '.Ids'"
|
||||
:prop="'roles.' + index + '.ids'"
|
||||
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
|
||||
<el-select v-model="role.ids" placeholder="选择工作空间" multiple>
|
||||
<el-option
|
||||
v-for="item in form.wsList"
|
||||
:key="item.id"
|
||||
|
@ -132,10 +132,10 @@
|
|||
</div>
|
||||
<div v-if="role.id ==='test_viewer'">
|
||||
<el-form-item label="选择工作空间"
|
||||
:prop="'roles.' + index + '.Ids'"
|
||||
:prop="'roles.' + index + '.ids'"
|
||||
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
|
||||
<el-select v-model="role.ids" placeholder="选择工作空间" multiple>
|
||||
<el-option
|
||||
v-for="item in form.wsList"
|
||||
:key="item.id"
|
||||
|
@ -181,7 +181,7 @@
|
|||
:prop="'roles.' + index + '.id'"
|
||||
:rules="{required: true, message: '请选择角色', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.id" placeholder="选择角色类型">
|
||||
<el-select v-model="role.id" placeholder="选择角色类型" :disabled="!!role.id">
|
||||
<el-option
|
||||
v-for="item in activeRole(role)"
|
||||
:key="item.id"
|
||||
|
@ -194,10 +194,10 @@
|
|||
</el-form-item>
|
||||
<div v-if="role.id === 'org_admin'">
|
||||
<el-form-item label="选择组织"
|
||||
:prop="'roles.' + index + '.Ids'"
|
||||
:prop="'roles.' + index + '.ids'"
|
||||
:rules="{required: true, message: '请选择组织', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.Ids" placeholder="选择组织" multiple>
|
||||
<el-select v-model="role.ids" placeholder="选择组织" multiple>
|
||||
<el-option
|
||||
v-for="item in form.orgList"
|
||||
:key="item.id"
|
||||
|
@ -209,10 +209,10 @@
|
|||
</div>
|
||||
<div v-if="role.id === 'test_manager'">
|
||||
<el-form-item label="选择工作空间"
|
||||
:prop="'roles.' + index + '.Ids'"
|
||||
:prop="'roles.' + index + '.ids'"
|
||||
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
|
||||
<el-select v-model="role.ids" placeholder="选择工作空间" multiple>
|
||||
<el-option
|
||||
v-for="item in form.wsList"
|
||||
:key="item.id"
|
||||
|
@ -224,10 +224,10 @@
|
|||
</div>
|
||||
<div v-if="role.id ==='test_user'">
|
||||
<el-form-item label="选择工作空间"
|
||||
:prop="'roles.' + index + '.Ids'"
|
||||
:prop="'roles.' + index + '.ids'"
|
||||
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
|
||||
<el-select v-model="role.ids" placeholder="选择工作空间" multiple>
|
||||
<el-option
|
||||
v-for="item in form.wsList"
|
||||
:key="item.id"
|
||||
|
@ -239,10 +239,10 @@
|
|||
</div>
|
||||
<div v-if="role.id ==='test_viewer'">
|
||||
<el-form-item label="选择工作空间"
|
||||
:prop="'roles.' + index + '.Ids'"
|
||||
:prop="'roles.' + index + '.ids'"
|
||||
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}"
|
||||
>
|
||||
<el-select v-model="role.Ids" placeholder="选择工作空间" multiple>
|
||||
<el-select v-model="role.ids" placeholder="选择工作空间" multiple>
|
||||
<el-option
|
||||
v-for="item in form.wsList"
|
||||
:key="item.id"
|
||||
|
|
|
@ -332,6 +332,7 @@ export default {
|
|||
result: "Result",
|
||||
success: "Success",
|
||||
fail: "Fail",
|
||||
total: "Total",
|
||||
test_name: "Test"
|
||||
},
|
||||
test_track: {
|
||||
|
|
|
@ -329,6 +329,7 @@ export default {
|
|||
result: "结果",
|
||||
success: "成功",
|
||||
fail: "失败",
|
||||
total: "全部",
|
||||
test_name: "所属测试"
|
||||
},
|
||||
test_track: {
|
||||
|
|
|
@ -323,6 +323,7 @@ export default {
|
|||
result: "結果",
|
||||
success: "成功",
|
||||
fail: "失敗",
|
||||
total: "全部",
|
||||
test_name: "所屬測試"
|
||||
},
|
||||
test_track: {
|
||||
|
|
Loading…
Reference in New Issue