生成jmx
This commit is contained in:
parent
181c406826
commit
d97f72315c
|
@ -75,6 +75,7 @@ public class ApiTestService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(DeleteAPITestRequest request) {
|
public void delete(DeleteAPITestRequest request) {
|
||||||
|
deleteFileByTestId(request.getId());
|
||||||
apiTestMapper.deleteByPrimaryKey(request.getId());
|
apiTestMapper.deleteByPrimaryKey(request.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,6 @@
|
||||||
<script>
|
<script>
|
||||||
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
|
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
|
||||||
import {Test} from "./model/ScenarioModel"
|
import {Test} from "./model/ScenarioModel"
|
||||||
import Jmx from "./model/JMX";
|
|
||||||
import {get, post, scenario} from "./model/test"
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiTestConfig",
|
name: "MsApiTestConfig",
|
||||||
|
@ -107,7 +105,8 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
cancel: function () {
|
cancel: function () {
|
||||||
this.$router.push('/api/test/list/all');
|
// this.$router.push('/api/test/list/all');
|
||||||
|
console.log(this.test.toJMX().xml);
|
||||||
},
|
},
|
||||||
getOptions: function (url) {
|
getOptions: function (url) {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
|
@ -122,10 +121,10 @@
|
||||||
formData.append('request', new Blob([requestJson], {
|
formData.append('request', new Blob([requestJson], {
|
||||||
type: "application/json"
|
type: "application/json"
|
||||||
}));
|
}));
|
||||||
|
let jmx = this.test.toJMX();
|
||||||
let jmx = new Jmx(get, "baidu", ["www.baidu.com"]).generate();
|
console.log(jmx.xml);
|
||||||
let blob = new Blob([jmx], {type: "application/octet-stream"});
|
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
|
||||||
formData.append("files", new File([blob], "baidu.jmx"));
|
formData.append("files", new File([blob], jmx.name));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<el-col class="assertion-select">
|
<el-col class="assertion-select">
|
||||||
<el-select class="assertion-item" v-model="regex.subject" size="small"
|
<el-select class="assertion-item" v-model="regex.subject" size="small"
|
||||||
:placeholder="$t('api_test.request.assertions.select_subject')">
|
:placeholder="$t('api_test.request.assertions.select_subject')">
|
||||||
<el-option label="HttpCode" value="HTTP_CODE"/>
|
<el-option label="Response Code" :value="subjects.RESPONSE_CODE"/>
|
||||||
<el-option label="Header" value="HEADER"/>
|
<el-option label="Response Headers" :value="subjects.RESPONSE_HEADERS"/>
|
||||||
<el-option label="Body" value="BODY"/>
|
<el-option label="Response Data" :value="subjects.RESPONSE_DATA"/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {Regex} from "../model/ScenarioModel";
|
import {ASSERTION_REGEX_SUBJECT, Regex} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiAssertionRegex",
|
name: "MsApiAssertionRegex",
|
||||||
|
@ -42,6 +42,12 @@
|
||||||
list: Array
|
list: Array
|
||||||
},
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
subjects: ASSERTION_REGEX_SUBJECT,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
add: function () {
|
add: function () {
|
||||||
this.list.push(new Regex(this.regex));
|
this.list.push(new Regex(this.regex));
|
||||||
|
|
|
@ -21,12 +21,12 @@
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
edit: Boolean,
|
edit: Boolean,
|
||||||
responseTime: ResponseTime
|
duration: ResponseTime
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
time: this.responseTime.responseInTime
|
time: this.duration.value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -34,11 +34,11 @@
|
||||||
add: function () {
|
add: function () {
|
||||||
this.remove();
|
this.remove();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.responseTime.responseInTime = this.time;
|
this.duration.value = this.time;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
remove: function () {
|
remove: function () {
|
||||||
this.responseTime.responseInTime = null;
|
this.duration.value = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<el-col class="assertion-select">
|
<el-col class="assertion-select">
|
||||||
<el-select class="assertion-item" v-model="subject" size="small"
|
<el-select class="assertion-item" v-model="subject" size="small"
|
||||||
:placeholder="$t('api_test.request.assertions.select_subject')">
|
:placeholder="$t('api_test.request.assertions.select_subject')">
|
||||||
<el-option label="HttpCode" value="HTTP_CODE"/>
|
<el-option label="Response Code" :value="subjects.RESPONSE_CODE"/>
|
||||||
<el-option label="Header" value="HEADER"/>
|
<el-option label="Response Headers" :value="subjects.RESPONSE_HEADERS"/>
|
||||||
<el-option label="Body" value="BODY"/>
|
<el-option label="Response Data" :value="subjects.RESPONSE_DATA"/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col class="assertion-select">
|
<el-col class="assertion-select">
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {Regex} from "../model/ScenarioModel";
|
import {Regex, ASSERTION_REGEX_SUBJECT} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiAssertionText",
|
name: "MsApiAssertionText",
|
||||||
|
@ -42,6 +42,7 @@
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
subjects: ASSERTION_REGEX_SUBJECT,
|
||||||
subject: "",
|
subject: "",
|
||||||
condition: "",
|
condition: "",
|
||||||
value: ""
|
value: ""
|
||||||
|
@ -54,27 +55,27 @@
|
||||||
},
|
},
|
||||||
toRegex: function () {
|
toRegex: function () {
|
||||||
let expression = "";
|
let expression = "";
|
||||||
let description = "";
|
let description = this.subject;
|
||||||
switch (this.condition) {
|
switch (this.condition) {
|
||||||
case "CONTAINS":
|
case "CONTAINS":
|
||||||
expression = ".*" + this.value + ".*";
|
expression = ".*" + this.value + ".*";
|
||||||
description = "contains: " + this.value;
|
description += " contains: " + this.value;
|
||||||
break;
|
break;
|
||||||
case "NOT_CONTAINS":
|
case "NOT_CONTAINS":
|
||||||
expression = "^((?!" + this.value + ").)*$";
|
expression = "^((?!" + this.value + ").)*$";
|
||||||
description = "not contains: " + this.value;
|
description += " not contains: " + this.value;
|
||||||
break;
|
break;
|
||||||
case "EQUALS":
|
case "EQUALS":
|
||||||
expression = "^" + this.value + "$";
|
expression = "^" + this.value + "$";
|
||||||
description = "equals: " + this.value;
|
description += " equals: " + this.value;
|
||||||
break;
|
break;
|
||||||
case "START_WITH":
|
case "START_WITH":
|
||||||
expression = "^" + this.value;
|
expression = "^" + this.value;
|
||||||
description = "start with: " + this.value;
|
description += " start with: " + this.value;
|
||||||
break;
|
break;
|
||||||
case "END_WITH":
|
case "END_WITH":
|
||||||
expression = this.value + "$";
|
expression = this.value + "$";
|
||||||
description = "end with: " + this.value;
|
description += " end with: " + this.value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new Regex({
|
return new Regex({
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<el-col :span="20">
|
<el-col :span="20">
|
||||||
<ms-api-assertion-text :list="assertions.regex" v-if="type === options.TEXT"/>
|
<ms-api-assertion-text :list="assertions.regex" v-if="type === options.TEXT"/>
|
||||||
<ms-api-assertion-regex :list="assertions.regex" v-if="type === options.REGEX"/>
|
<ms-api-assertion-regex :list="assertions.regex" v-if="type === options.REGEX"/>
|
||||||
<ms-api-assertion-response-time :response-time="assertions.responseTime" v-if="type === options.RESPONSE_TIME"/>
|
<ms-api-assertion-response-time :duration="assertions.duration" v-if="type === options.RESPONSE_TIME"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,8 @@
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isShow() {
|
isShow() {
|
||||||
let rt = this.assertions.responseTime;
|
let rt = this.assertions.duration;
|
||||||
return rt.responseInTime !== null && rt.responseInTime > 0;
|
return rt.value !== null && rt.value > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
<el-radio-button :label="type.KV">
|
<el-radio-button :label="type.KV">
|
||||||
{{$t('api_test.request.body_kv')}}
|
{{$t('api_test.request.body_kv')}}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
<el-radio-button :label="type.TEXT">
|
<el-radio-button :label="type.RAW">
|
||||||
{{$t('api_test.request.body_text')}}
|
{{$t('api_test.request.body_text')}}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
|
|
||||||
<ms-api-key-value :items="body.kvs" v-if="body.isKV()"/>
|
<ms-api-key-value :items="body.kvs" v-if="body.isKV()"/>
|
||||||
|
|
||||||
<el-input class="textarea" type="textarea" v-model="body.text" :autosize="{ minRows: 10, maxRows: 25}" resize="none"
|
<el-input class="textarea" type="textarea" v-model="body.raw" :autosize="{ minRows: 10, maxRows: 25}" resize="none"
|
||||||
v-else/>
|
v-else/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -6,27 +6,14 @@
|
||||||
<div class="kv-row" v-for="(item, index) in items" :key="index">
|
<div class="kv-row" v-for="(item, index) in items" :key="index">
|
||||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
||||||
<el-col>
|
<el-col>
|
||||||
<el-input v-model="item.key" placeholder="Key" size="small" maxlength="100" @change="check"/>
|
<el-input v-model="item.name" placeholder="Key" size="small" maxlength="100" @change="change"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
<el-input v-model="item.value" placeholder="Value" size="small" maxlength="100" @change="check"/>
|
<el-input v-model="item.value" placeholder="Value" size="small" maxlength="100" @change="change"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col class="kv-delete">
|
<el-col class="kv-delete">
|
||||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"/>
|
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||||
</el-col>
|
:disabled="isDisable(index)"/>
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="kv-row">
|
|
||||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
|
||||||
<el-col>
|
|
||||||
<el-input v-model="kv.key" placeholder="Key" size="small" maxlength="100" @change="add"/>
|
|
||||||
</el-col>
|
|
||||||
<el-col>
|
|
||||||
<el-input v-model="kv.value" placeholder="Value" size="small" maxlength="100" @change="add"/>
|
|
||||||
</el-col>
|
|
||||||
<el-col class="kv-delete">
|
|
||||||
<el-button size="mini" class="el-icon-delete-solid" circle :disabled="true"/>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,33 +31,38 @@
|
||||||
items: Array
|
items: Array
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
methods: {
|
||||||
return {
|
remove: function (index) {
|
||||||
kv: new KeyValue()
|
this.items.splice(index, 1);
|
||||||
|
this.$emit('change', this.items);
|
||||||
|
},
|
||||||
|
change: function () {
|
||||||
|
let isNeedCreate = true;
|
||||||
|
let removeIndex = -1;
|
||||||
|
this.items.forEach((item, index) => {
|
||||||
|
if (!item.name && !item.value) {
|
||||||
|
// 多余的空行
|
||||||
|
if (index !== this.items.length - 1) {
|
||||||
|
removeIndex = index;
|
||||||
|
}
|
||||||
|
// 没有空行,需要创建空行
|
||||||
|
isNeedCreate = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (isNeedCreate) {
|
||||||
|
this.items.push(new KeyValue());
|
||||||
|
}
|
||||||
|
this.$emit('change', this.items);
|
||||||
|
// TODO 检查key重复
|
||||||
|
},
|
||||||
|
isDisable: function (index) {
|
||||||
|
return this.items.length - 1 === index;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
created() {
|
||||||
add: function () {
|
if (this.items.length === 0) {
|
||||||
if (this.kv.key || this.kv.value) {
|
this.items.push(new KeyValue());
|
||||||
this.items.push(this.kv);
|
|
||||||
this.kv = new KeyValue();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
remove: function (index) {
|
|
||||||
this.items.splice(index, 1);
|
|
||||||
},
|
|
||||||
check: function () {
|
|
||||||
let removeIndex = -1;
|
|
||||||
this.items.forEach((item, index) => {
|
|
||||||
if (!item.key && !item.value) {
|
|
||||||
removeIndex = index;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (removeIndex !== -1) {
|
|
||||||
this.remove(removeIndex);
|
|
||||||
}
|
|
||||||
// TODO 检查key重复
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
computed: {
|
computed: {
|
||||||
isSelected() {
|
isSelected() {
|
||||||
return function (request) {
|
return function (request) {
|
||||||
return this.selected.randomId === request.randomId;
|
return this.selected.id === request.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="$t('api_test.request.url')" prop="url">
|
<el-form-item :label="$t('api_test.request.url')" prop="url">
|
||||||
<el-input v-model="request.url" maxlength="100" :placeholder="$t('api_test.request.url_description')">
|
<el-input v-model="request.url" maxlength="100" :placeholder="$t('api_test.request.url_description')"
|
||||||
<el-select v-model="request.method" slot="prepend" class="request-method-select">
|
@change="urlChange" clearable>
|
||||||
|
<el-select v-model="request.method" slot="prepend" class="request-method-select" @change="methodChange">
|
||||||
<el-option label="GET" value="GET"/>
|
<el-option label="GET" value="GET"/>
|
||||||
<el-option label="POST" value="POST"/>
|
<el-option label="POST" value="POST"/>
|
||||||
<el-option label="PUT" value="PUT"/>
|
<el-option label="PUT" value="PUT"/>
|
||||||
|
@ -21,7 +22,8 @@
|
||||||
|
|
||||||
<el-tabs v-model="activeName">
|
<el-tabs v-model="activeName">
|
||||||
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
|
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
|
||||||
<ms-api-key-value :items="request.parameters" :description="$t('api_test.request.parameters_desc')"/>
|
<ms-api-key-value :items="request.parameters" :description="$t('api_test.request.parameters_desc')"
|
||||||
|
@change="parametersChange"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
|
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
|
||||||
<ms-api-key-value :items="request.headers"/>
|
<ms-api-key-value :items="request.headers"/>
|
||||||
|
@ -43,7 +45,7 @@
|
||||||
import MsApiKeyValue from "./ApiKeyValue";
|
import MsApiKeyValue from "./ApiKeyValue";
|
||||||
import MsApiBody from "./ApiBody";
|
import MsApiBody from "./ApiBody";
|
||||||
import MsApiAssertions from "./ApiAssertions";
|
import MsApiAssertions from "./ApiAssertions";
|
||||||
import {Request} from "../model/ScenarioModel";
|
import {KeyValue, Request} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiRequestForm",
|
name: "MsApiRequestForm",
|
||||||
|
@ -66,6 +68,48 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
urlChange() {
|
||||||
|
if (!this.request.url) return;
|
||||||
|
|
||||||
|
let parameters = [];
|
||||||
|
let url = new URL(this.addProtocol(this.request.url));
|
||||||
|
url.searchParams.forEach(function (key, value) {
|
||||||
|
if (key && value) {
|
||||||
|
parameters.push(new KeyValue({name: key, value: value}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 添加一个空的,用于填写
|
||||||
|
parameters.push(new KeyValue());
|
||||||
|
this.request.parameters = parameters;
|
||||||
|
this.request.url = url.toString();
|
||||||
|
},
|
||||||
|
methodChange(value) {
|
||||||
|
if (value === 'GET' && this.activeName === 'body') {
|
||||||
|
this.activeName = 'parameters';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parametersChange(parameters) {
|
||||||
|
if (!this.request.url) return;
|
||||||
|
let url = new URL(this.addProtocol(this.request.url));
|
||||||
|
url.search = "";
|
||||||
|
parameters.forEach(function (parameter) {
|
||||||
|
if (parameter.name && parameter.value) {
|
||||||
|
url.searchParams.append(parameter.name, parameter.value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.request.url = url.toString();
|
||||||
|
},
|
||||||
|
addProtocol(url) {
|
||||||
|
if (url) {
|
||||||
|
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
|
||||||
|
return "https://" + url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isNotGet() {
|
isNotGet() {
|
||||||
return this.request.method !== "GET";
|
return this.request.method !== "GET";
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
{{$t('api_test.scenario.config')}}
|
{{$t('api_test.scenario.config')}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 暂时去掉,将来再-->
|
<!-- 暂时去掉,将来再加-->
|
||||||
<!-- <el-dropdown trigger="click" @command="handleCommand">-->
|
<!-- <el-dropdown trigger="click" @command="handleCommand">-->
|
||||||
<!-- <span class="el-dropdown-link el-icon-more scenario-btn"/>-->
|
<!-- <span class="el-dropdown-link el-icon-more scenario-btn"/>-->
|
||||||
<!-- <el-dropdown-menu slot="dropdown">-->
|
<!-- <el-dropdown-menu slot="dropdown">-->
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
</ms-api-collapse-item>
|
</ms-api-collapse-item>
|
||||||
</ms-api-collapse>
|
</ms-api-collapse>
|
||||||
</div>
|
</div>
|
||||||
<!-- 暂时去掉,将来再-->
|
<!-- 暂时去掉,将来再加-->
|
||||||
<!-- <el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createScenario"/>-->
|
<!-- <el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createScenario"/>-->
|
||||||
</el-aside>
|
</el-aside>
|
||||||
|
|
||||||
|
|
|
@ -4,18 +4,18 @@
|
||||||
<el-input v-model="scenario.name" maxlength="100"/>
|
<el-input v-model="scenario.name" maxlength="100"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="$t('api_test.scenario.base_url')" prop="url">
|
<!-- <el-form-item :label="$t('api_test.scenario.base_url')" prop="url">-->
|
||||||
<el-input :placeholder="$t('api_test.scenario.base_url_description')" v-model="scenario.url" maxlength="100"/>
|
<!-- <el-input :placeholder="$t('api_test.scenario.base_url_description')" v-model="scenario.url" maxlength="100"/>-->
|
||||||
</el-form-item>
|
<!-- </el-form-item>-->
|
||||||
|
|
||||||
<el-tabs v-model="activeName">
|
<!-- <el-tabs v-model="activeName">-->
|
||||||
<el-tab-pane :label="$t('api_test.scenario.variables')" name="variables">
|
<!-- <el-tab-pane :label="$t('api_test.scenario.parameters')" name="parameters">-->
|
||||||
<ms-api-key-value :items="scenario.variables"/>
|
<!-- <ms-api-key-value :items="scenario.parameters" :description="$t('api_test.scenario.kv_description')"/>-->
|
||||||
</el-tab-pane>
|
<!-- </el-tab-pane>-->
|
||||||
<el-tab-pane :label="$t('api_test.scenario.headers')" name="headers">
|
<!-- <el-tab-pane :label="$t('api_test.scenario.headers')" name="headers">-->
|
||||||
<ms-api-key-value :items="scenario.headers"/>
|
<!-- <ms-api-key-value :items="scenario.headers" :description="$t('api_test.scenario.kv_description')"/>-->
|
||||||
</el-tab-pane>
|
<!-- </el-tab-pane>-->
|
||||||
</el-tabs>
|
<!-- </el-tabs>-->
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
activeName: "variables",
|
activeName: "parameters",
|
||||||
rules: {
|
rules: {
|
||||||
name: [
|
name: [
|
||||||
{max: 100, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'}
|
{max: 100, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'}
|
||||||
|
|
|
@ -1,619 +1,371 @@
|
||||||
let Xml4js = function (options = {}) {
|
const INDENT = ' ';
|
||||||
this.depth = 0;
|
|
||||||
this.encoding = options.encoding || 'UTF-8';
|
|
||||||
this.xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
||||||
};
|
|
||||||
|
|
||||||
Xml4js.prototype = {
|
export class Element {
|
||||||
toString: function () {
|
constructor(name, attributes, value) {
|
||||||
return this.xml;
|
this.indent = '';
|
||||||
},
|
this.name = name;
|
||||||
write: function () {
|
this.attributes = attributes || {};
|
||||||
let context = this,
|
this.value = undefined;
|
||||||
elem,
|
this.elements = [];
|
||||||
attr = null,
|
|
||||||
value = null,
|
|
||||||
callback = false,
|
|
||||||
fn = null,
|
|
||||||
hasValue = null,
|
|
||||||
empty = null;
|
|
||||||
|
|
||||||
elem = arguments[0];
|
if (value instanceof Element) {
|
||||||
|
this.elements.push(value);
|
||||||
if (elem === '<hashTree/>') {
|
|
||||||
this.xml += '<hashTree/>\n'
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof arguments[1] == 'object') {
|
|
||||||
attr = arguments[1];
|
|
||||||
} else if (typeof arguments[1] == 'function') {
|
|
||||||
callback = true;
|
|
||||||
fn = arguments[1];
|
|
||||||
} else {
|
} else {
|
||||||
value = arguments[1];
|
this.value = value;
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof arguments[2] == 'function') {
|
|
||||||
if (!callback) {
|
|
||||||
callback = true;
|
|
||||||
fn = arguments[2];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (value === null) {
|
|
||||||
value = arguments[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hasValue = value !== null;
|
|
||||||
empty = !hasValue && !callback;
|
|
||||||
|
|
||||||
function indent(depth) {
|
|
||||||
if (depth == null)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
let space = '';
|
|
||||||
for (let i = 0; i < depth; i++)
|
|
||||||
if (context.tabs)
|
|
||||||
space += "\t";
|
|
||||||
else
|
|
||||||
space += ' ';
|
|
||||||
|
|
||||||
return space;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elem === 'cdata') {
|
|
||||||
this.xml += indent(this.depth) + '<![CDATA[' + value + ']]>\n';
|
|
||||||
} else {
|
|
||||||
this.xml += indent(this.depth) + '<' + elem;
|
|
||||||
if (attr) {
|
|
||||||
for (let key in attr) {
|
|
||||||
if (attr.hasOwnProperty(key)) {
|
|
||||||
this.xml += ' ';
|
|
||||||
this.xml += key + '="' + attr[key] + '"';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value !== null)
|
|
||||||
this.xml += '>' + value + '</' + elem + '>\n';
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
this.xml += '>\n'
|
|
||||||
this.depth++;
|
|
||||||
if (fn instanceof Function) {
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
this.depth--;
|
|
||||||
this.xml += indent(this.depth) + '</' + elem + '>\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty)
|
|
||||||
this.xml += '/>\n';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
set(value) {
|
||||||
*
|
this.elements = [];
|
||||||
*JMX 构造对象
|
this.value = value;
|
||||||
*/
|
|
||||||
let Jmx = function (data, name, ds) {
|
|
||||||
this.data = data;
|
|
||||||
this.name = name;
|
|
||||||
let domains = {};
|
|
||||||
ds.forEach(function (d) {
|
|
||||||
domains[d] = d;
|
|
||||||
});
|
|
||||||
this.domains = domains;
|
|
||||||
|
|
||||||
this.xml = new Xml4js();
|
|
||||||
};
|
|
||||||
|
|
||||||
Jmx.prototype = {
|
|
||||||
generate: function () {
|
|
||||||
generateXml(this.xml, this.preprocessing());
|
|
||||||
return this.xml.toString();
|
|
||||||
},
|
|
||||||
preprocessing: function () {
|
|
||||||
let domainAlias = {};
|
|
||||||
let index = 1;
|
|
||||||
Object.getOwnPropertyNames(this.domains).forEach(function (key) {
|
|
||||||
domainAlias[key] = "BASE_URL_" + index;
|
|
||||||
index = index + 1;
|
|
||||||
});
|
|
||||||
let UserAgent = null;
|
|
||||||
let hsps = [];
|
|
||||||
let domains = this.domains;
|
|
||||||
|
|
||||||
this.data.forEach(function (item) {
|
|
||||||
let url = new URL(item.url);
|
|
||||||
if (domains[url.hostname]) {
|
|
||||||
let hsp = new HTTPSamplerProxy(item.label.replace(/&/gi, "&"));
|
|
||||||
hsp.stringProp("HTTPSampler.domain", "${" + domainAlias[url.hostname] + "}");
|
|
||||||
hsp.stringProp("HTTPSampler.protocol", url.protocol.split(":")[0]);
|
|
||||||
hsp.stringProp("HTTPSampler.path", url.pathname);
|
|
||||||
hsp.stringProp("HTTPSampler.method", item.method);
|
|
||||||
if (url.port === "") {
|
|
||||||
hsp.intProp("HTTPSampler.port", 0);
|
|
||||||
} else {
|
|
||||||
hsp.intProp("HTTPSampler.port", url.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
hsp.addArguments(item.url, item.body);
|
|
||||||
hsps.push(hsp);
|
|
||||||
|
|
||||||
if (item.headers.length === 0) {
|
|
||||||
hsps.push("<hashTree/>");
|
|
||||||
} else {
|
|
||||||
let cphmh = new CollectionPropHeaderManagerHeaders();
|
|
||||||
|
|
||||||
item.headers.forEach(function (h) {
|
|
||||||
if (h.name === 'User-Agent') {
|
|
||||||
UserAgent = h.value;
|
|
||||||
} else {
|
|
||||||
cphmh.addValue(new elementPropHeaderManager(h.name, h.value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add Header
|
|
||||||
let ephm = new HeaderManager();
|
|
||||||
ephm.addValue(cphmh);
|
|
||||||
let ht = new HashTree();
|
|
||||||
ht.addValue(ephm);
|
|
||||||
ht.addValue("<hashTree/>");
|
|
||||||
hsps.push(ht);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
let jmeterTestPlan = new JmeterTestPlan();
|
|
||||||
let hashTree = new HashTree();
|
|
||||||
jmeterTestPlan.addValue(hashTree);
|
|
||||||
|
|
||||||
let testPlan = new TestPlan(this.name);
|
|
||||||
testPlan.boolProp("TestPlan.functional_mode", false);
|
|
||||||
testPlan.boolProp("TestPlan.serialize_threadgroups", false);
|
|
||||||
testPlan.boolProp("TestPlan.tearDown_on_shutdown", true);
|
|
||||||
testPlan.stringProp("TestPlan.comments", "");
|
|
||||||
testPlan.stringProp("TestPlan.user_define_classpath", "");
|
|
||||||
testPlan.addArguments();
|
|
||||||
hashTree.addValue(testPlan);
|
|
||||||
|
|
||||||
let hashTree1 = new HashTree();
|
|
||||||
hashTree.addValue(hashTree1);
|
|
||||||
|
|
||||||
//HeaderManager
|
|
||||||
let hm = new HeaderManager();
|
|
||||||
hm.userAgent(UserAgent);
|
|
||||||
hashTree1.addValue(hm);
|
|
||||||
hashTree1.addValue("<hashTree/>");
|
|
||||||
|
|
||||||
//Arguments
|
|
||||||
let arg = new Arguments();
|
|
||||||
arg.addArguments(domainAlias);
|
|
||||||
hashTree1.addValue(arg);
|
|
||||||
hashTree1.addValue("<hashTree/>");
|
|
||||||
|
|
||||||
//DNSCacheManager
|
|
||||||
let dns = new DNSCacheManager();
|
|
||||||
dns.init();
|
|
||||||
hashTree1.addValue(dns);
|
|
||||||
hashTree1.addValue("<hashTree/>");
|
|
||||||
|
|
||||||
//AuthManager
|
|
||||||
let auth = new AuthManager();
|
|
||||||
auth.init();
|
|
||||||
hashTree1.addValue(auth);
|
|
||||||
hashTree1.addValue("<hashTree/>");
|
|
||||||
|
|
||||||
//CookieManager
|
|
||||||
let cookie = new CookieManager();
|
|
||||||
cookie.init();
|
|
||||||
hashTree1.addValue(cookie);
|
|
||||||
hashTree1.addValue("<hashTree/>");
|
|
||||||
|
|
||||||
//CacheManager
|
|
||||||
let cache = new CacheManager();
|
|
||||||
cache.boolProp("clearEachIteration", true);
|
|
||||||
cache.boolProp("useExpires", false);
|
|
||||||
cache.boolProp("CacheManager.controlledByThread", false);
|
|
||||||
hashTree1.addValue(cache);
|
|
||||||
hashTree1.addValue("<hashTree/>");
|
|
||||||
|
|
||||||
let threadGroup = new ThreadGroup();
|
|
||||||
hashTree1.addValue(threadGroup);
|
|
||||||
threadGroup.intProp("ThreadGroup.num_threads", 1);
|
|
||||||
threadGroup.intProp("ThreadGroup.ramp_time", 1);
|
|
||||||
threadGroup.longProp("ThreadGroup.delay", 0);
|
|
||||||
threadGroup.longProp("ThreadGroup.duration", 0);
|
|
||||||
threadGroup.stringProp("ThreadGroup.on_sample_error", "continue");
|
|
||||||
threadGroup.boolProp("ThreadGroup.scheduler", false);
|
|
||||||
|
|
||||||
let lc = new LoopController();
|
|
||||||
threadGroup.addValue(lc);
|
|
||||||
lc.boolProp("LoopController.continue_forever", false);
|
|
||||||
lc.stringProp("LoopController.loops", 1);
|
|
||||||
|
|
||||||
let ht = new HashTree();
|
|
||||||
|
|
||||||
hashTree1.addValue(ht);
|
|
||||||
|
|
||||||
hsps.forEach(function (hsp) {
|
|
||||||
ht.addValue(hsp);
|
|
||||||
});
|
|
||||||
return jmeterTestPlan;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
add(element) {
|
||||||
|
if (element instanceof Element) {
|
||||||
|
this.value = undefined;
|
||||||
|
this.elements.push(element);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function generateXml(xml, hashTree) {
|
commonValue(tag, name, value) {
|
||||||
if (hashTree instanceof HashTree) {
|
return this.add(new Element(tag, {name: name}, value));
|
||||||
if (hashTree.values.length > 0 && !(hashTree.values[0] instanceof HashTree)) {
|
}
|
||||||
xml.write(hashTree.element, hashTree.attributes, hashTree.values[0]);
|
|
||||||
|
boolProp(name, value) {
|
||||||
|
return this.commonValue('boolProp', name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
intProp(name, value) {
|
||||||
|
return this.commonValue('intProp', name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
longProp(name, value) {
|
||||||
|
return this.commonValue('longProp', name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
stringProp(name, value) {
|
||||||
|
return this.commonValue('stringProp', name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionProp(name) {
|
||||||
|
return this.commonValue('collectionProp', name);
|
||||||
|
}
|
||||||
|
|
||||||
|
elementProp(name, elementType) {
|
||||||
|
return this.add(new Element('elementProp', {name: name, elementType: elementType}));
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyValue() {
|
||||||
|
return this.value === undefined || this.value === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyElement() {
|
||||||
|
return this.elements.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty() {
|
||||||
|
return this.isEmptyValue() && this.isEmptyElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
toXML(indent) {
|
||||||
|
if (indent) {
|
||||||
|
this.indent = indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = this.start();
|
||||||
|
str += this.content();
|
||||||
|
str += this.end();
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
let str = this.indent + '<' + this.name;
|
||||||
|
for (let key in this.attributes) {
|
||||||
|
if (this.attributes.hasOwnProperty(key)) {
|
||||||
|
str += ' ' + key + '="' + this.attributes[key] + '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.isEmpty()) {
|
||||||
|
str += '/>';
|
||||||
} else {
|
} else {
|
||||||
xml.write(hashTree.element, hashTree.attributes, function () {
|
str += '>';
|
||||||
if (hashTree.values.length > 0) {
|
}
|
||||||
hashTree.values.forEach(function (v) {
|
return str;
|
||||||
generateXml(xml, v);
|
}
|
||||||
})
|
|
||||||
}
|
content() {
|
||||||
|
if (!this.isEmptyValue()) {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = '';
|
||||||
|
let parent = this;
|
||||||
|
if (this.elements.length > 0) {
|
||||||
|
str += '\n';
|
||||||
|
this.elements.forEach(e => {
|
||||||
|
e.indent += parent.indent + INDENT;
|
||||||
|
str += e.toXML();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
return str;
|
||||||
xml.write(hashTree);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
end() {
|
||||||
}
|
if (this.isEmpty()) {
|
||||||
|
return '\n';
|
||||||
|
}
|
||||||
/**
|
let str = '</' + this.name + '>\n';
|
||||||
* HashTree 初始对象
|
if (!this.isEmptyValue()) {
|
||||||
*/
|
return str;
|
||||||
let HashTree = function (element, attributes, value) {
|
}
|
||||||
this.element = element || "hashTree";
|
if (!this.isEmptyElement()) {
|
||||||
this.attributes = attributes || {};
|
return this.indent + str;
|
||||||
this.values = [];
|
|
||||||
if (value !== undefined) {
|
|
||||||
this.values.push(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
HashTree.prototype.addValue = function (hashTree) {
|
|
||||||
this.values.push(hashTree)
|
|
||||||
};
|
|
||||||
|
|
||||||
HashTree.prototype.commonValue = function (element, name, value) {
|
|
||||||
this.values.push(new HashTree(element, {name: name}, value))
|
|
||||||
};
|
|
||||||
|
|
||||||
HashTree.prototype.intProp = function (name, value) {
|
|
||||||
this.commonValue("intProp", name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
HashTree.prototype.longProp = function (name, value) {
|
|
||||||
this.commonValue("longProp", name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
HashTree.prototype.stringProp = function (name, value) {
|
|
||||||
this.commonValue("stringProp", name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
HashTree.prototype.boolProp = function (name, value) {
|
|
||||||
this.commonValue("boolProp", name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HashTree 的帮助对象
|
|
||||||
*/
|
|
||||||
|
|
||||||
let CHashTree = function (element, guiclass, testclass, testname, enabled) {
|
|
||||||
HashTree.call(this, element, {
|
|
||||||
guiclass: guiclass,
|
|
||||||
testclass: testclass,
|
|
||||||
testname: testname || "TEST",
|
|
||||||
enabled: enabled || "true"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
CHashTree.prototype = new HashTree();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JmeterTestPlan
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
let JmeterTestPlan = function (options = {}) {
|
|
||||||
HashTree.call(this, "jmeterTestPlan",
|
|
||||||
{
|
|
||||||
version: options.version || "1.2",
|
|
||||||
properties: options.properties || "5.0",
|
|
||||||
jmeter: options.jmeter || "5.2.1"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
JmeterTestPlan.prototype = new HashTree();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TestPlan
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
let TestPlan = function (name) {
|
|
||||||
CHashTree.call(this, "TestPlan", "TestPlanGui", "TestPlan", name);
|
|
||||||
};
|
|
||||||
TestPlan.prototype = new CHashTree();
|
|
||||||
|
|
||||||
TestPlan.prototype.addArguments = function () {
|
|
||||||
let ht = new HashTree("elementProp", {
|
|
||||||
name: "TestPlan.user_defined_variables",
|
|
||||||
elementType: "Arguments"
|
|
||||||
});
|
|
||||||
ht.addValue(new HashTree("collectionProp", {name: "Arguments.arguments"}));
|
|
||||||
this.values.push(ht);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HeaderManager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
let HeaderManager = function () {
|
|
||||||
CHashTree.call(this, "HeaderManager", "HeaderPanel", "HeaderManager", "HTTP Header manager");
|
|
||||||
};
|
|
||||||
HeaderManager.prototype = new CHashTree();
|
|
||||||
|
|
||||||
HeaderManager.prototype.userAgent = function (userAgent) {
|
|
||||||
let cp = new HashTree("collectionProp", {
|
|
||||||
name: "HeaderManager.headers",
|
|
||||||
});
|
|
||||||
|
|
||||||
let ep = new HashTree("elementProp", {
|
|
||||||
name: "User-Agent",
|
|
||||||
elementType: "Header"
|
|
||||||
});
|
|
||||||
|
|
||||||
ep.stringProp("Header.name", "User-Agent");
|
|
||||||
ep.stringProp("Header.value", userAgent);
|
|
||||||
|
|
||||||
cp.addValue(ep);
|
|
||||||
|
|
||||||
this.values.push(cp);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arguments
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
let Arguments = function () {
|
|
||||||
CHashTree.call(this, "Arguments", "ArgumentsPanel", "Arguments", "User Defined Variables");
|
|
||||||
};
|
|
||||||
Arguments.prototype = new CHashTree();
|
|
||||||
|
|
||||||
Arguments.prototype.addArguments = function (domianAlias) {
|
|
||||||
let cp = new HashTree("collectionProp", {name: "Arguments.arguments"});
|
|
||||||
|
|
||||||
if (domianAlias) {
|
|
||||||
Object.getOwnPropertyNames(domianAlias).forEach(function (key) {
|
|
||||||
cp.addValue(addArgumentsCommon(domianAlias[key], key, true, true));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.values.push(cp);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DNSCacheManager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
let DNSCacheManager = function () {
|
|
||||||
CHashTree.call(this, "DNSCacheManager", "DNSCachePanel", "DNSCacheManager", "DNS Cache Manager");
|
|
||||||
};
|
|
||||||
DNSCacheManager.prototype = new CHashTree();
|
|
||||||
|
|
||||||
DNSCacheManager.prototype.init = function () {
|
|
||||||
let cp = new HashTree("collectionProp", {name: "DNSCacheManager.servers"});
|
|
||||||
this.values.push(cp);
|
|
||||||
this.boolProp("DNSCacheManager.clearEachIteration", true);
|
|
||||||
this.boolProp("DNSCacheManager.isCustomResolver", false);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AuthManager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
let AuthManager = function () {
|
|
||||||
CHashTree.call(this, "AuthManager", "AuthPanel", "AuthManager", "HTTP Authorization Manager");
|
|
||||||
};
|
|
||||||
AuthManager.prototype = new CHashTree();
|
|
||||||
AuthManager.prototype.init = function () {
|
|
||||||
let cp = new HashTree("collectionProp", {name: "AuthManager.auth_list"});
|
|
||||||
this.values.push(cp);
|
|
||||||
this.boolProp("AuthManager.controlledByThreadGroup", false);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CookieManager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
let CookieManager = function () {
|
|
||||||
CHashTree.call(this, "CookieManager", "CookiePanel", "CookieManager", "HTTP Cookie Manager");
|
|
||||||
};
|
|
||||||
CookieManager.prototype = new CHashTree();
|
|
||||||
CookieManager.prototype.init = function () {
|
|
||||||
let cp = new HashTree("collectionProp", {name: "CookieManager.cookies"});
|
|
||||||
this.values.push(cp);
|
|
||||||
this.boolProp("CookieManager.clearEachIteration", true);
|
|
||||||
this.boolProp("CookieManager.controlledByThreadGroup", false);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CacheManager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
let CacheManager = function () {
|
|
||||||
CHashTree.call(this, "CacheManager", "CacheManagerGui", "CacheManager", "HTTP Cache Manager");
|
|
||||||
};
|
|
||||||
CacheManager.prototype = new CHashTree();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ThreadGroup
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
let ThreadGroup = function () {
|
|
||||||
CHashTree.call(this, "ThreadGroup", "ThreadGroupGui", "ThreadGroup", "Group1");
|
|
||||||
};
|
|
||||||
ThreadGroup.prototype = new CHashTree();
|
|
||||||
|
|
||||||
|
|
||||||
let LoopController = function () {
|
|
||||||
HashTree.call(this, "elementProp", {
|
|
||||||
name: "ThreadGroup.main_controller",
|
|
||||||
elementType: "LoopController",
|
|
||||||
guiclass: "LoopControlPanel",
|
|
||||||
testclass: "LoopController",
|
|
||||||
testname: "Loop Controller",
|
|
||||||
enabled: "true"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
LoopController.prototype = new HashTree();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTPSamplerProxy
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
let HTTPSamplerProxy = function (name) {
|
|
||||||
HashTree.call(this, "HTTPSamplerProxy", {
|
|
||||||
guiclass: "HttpTestSampleGui",
|
|
||||||
testclass: "HTTPSamplerProxy",
|
|
||||||
testname: name,
|
|
||||||
enabled: "true"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
HTTPSamplerProxy.prototype = new HashTree();
|
|
||||||
|
|
||||||
HTTPSamplerProxy.prototype.addArguments = function (url, body) {
|
|
||||||
let ht = new HashTree("elementProp", {
|
|
||||||
name: "HTTPSampler.Arguments",
|
|
||||||
elementType: "Arguments",
|
|
||||||
guiclass: "HTTPArgumentsPanel",
|
|
||||||
testclass: "Arguments",
|
|
||||||
enabled: "true"
|
|
||||||
});
|
|
||||||
let cp = new HashTree("collectionProp", {name: "Arguments.arguments"});
|
|
||||||
ht.addValue(cp);
|
|
||||||
|
|
||||||
let params = getUrlParams(url);
|
|
||||||
params.forEach(function (item) {
|
|
||||||
cp.addValue(addArgumentsCommon(item.name, item.value, false));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (body) {
|
|
||||||
this.boolProp("HTTPSampler.postBodyRaw", true);
|
|
||||||
if (body instanceof Array) {
|
|
||||||
cp.addValue(addArgumentsBody(body[0], true));
|
|
||||||
} else {
|
|
||||||
cp.addValue(addArgumentsBody(body, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.values.push(ht);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function addArgumentsCommon(name, value, alwaysEncode, metadata) {
|
|
||||||
let ht = new HashTree("elementProp", {
|
|
||||||
name: name,
|
|
||||||
elementType: "HTTPArgument"
|
|
||||||
});
|
|
||||||
ht.boolProp("HTTPArgument.always_encode", alwaysEncode);
|
|
||||||
if (typeof (name) == 'string') {
|
|
||||||
ht.stringProp("Argument.name", name);
|
|
||||||
}
|
|
||||||
ht.stringProp("Argument.value", value);
|
|
||||||
if (!metadata) {
|
|
||||||
ht.stringProp("Argument.metadata", "=");
|
|
||||||
}
|
|
||||||
return ht;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addArgumentsBody(value, alwaysEncode, metadata) {
|
export class HashTree extends Element {
|
||||||
let ht = new HashTree("elementProp", {
|
constructor() {
|
||||||
name: name,
|
super('hashTree');
|
||||||
elementType: "HTTPArgument"
|
}
|
||||||
});
|
|
||||||
ht.boolProp("HTTPArgument.always_encode", alwaysEncode);
|
add(te) {
|
||||||
ht.stringProp("Argument.value", value);
|
if (te instanceof TestElement) {
|
||||||
if (!metadata) {
|
super.add(te);
|
||||||
ht.stringProp("Argument.metadata", "=");
|
}
|
||||||
}
|
}
|
||||||
return ht;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUrlParams(url) {
|
export class TestElement extends Element {
|
||||||
let params = [];
|
constructor(name, attributes, value) {
|
||||||
if (url.indexOf('?') === -1) {
|
// Element, 只能添加Element
|
||||||
return params;
|
super(name, attributes, value);
|
||||||
|
// HashTree, 只能添加TestElement
|
||||||
|
this.hashTree = new HashTree();
|
||||||
}
|
}
|
||||||
let queryString = url.split('?')[1];
|
|
||||||
queryString = queryString.split('#')[0];
|
|
||||||
|
|
||||||
let arr = queryString.split('&');
|
put(te) {
|
||||||
|
this.hashTree.add(te);
|
||||||
|
}
|
||||||
|
|
||||||
arr.forEach(function (item) {
|
toXML() {
|
||||||
let a = item.split('=');
|
let str = super.toXML();
|
||||||
params.push({
|
str += this.hashTree.toXML(this.indent);
|
||||||
name: a[0],
|
return str;
|
||||||
value: a[1]
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DefaultTestElement extends TestElement {
|
||||||
|
constructor(tag, guiclass, testclass, testname, enabled) {
|
||||||
|
super(tag, {
|
||||||
|
guiclass: guiclass,
|
||||||
|
testclass: testclass,
|
||||||
|
testname: testname || tag + ' Name',
|
||||||
|
enabled: enabled || true
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
return params;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TestPlan extends DefaultTestElement {
|
||||||
|
constructor(testName) {
|
||||||
|
super('TestPlan', 'TestPlanGui', 'TestPlan', testName || 'TestPlan');
|
||||||
|
|
||||||
let ElementPropHeaderManager = function () {
|
this.boolProp("TestPlan.functional_mode", false);
|
||||||
HashTree.call(this, "elementProp", {
|
this.boolProp("TestPlan.serialize_threadgroups", false);
|
||||||
name: "HTTPSampler.header_manager",
|
this.boolProp("TestPlan.tearDown_on_shutdown", true);
|
||||||
elementType: "HeaderManager"
|
this.stringProp("TestPlan.comments", "");
|
||||||
});
|
this.stringProp("TestPlan.user_define_classpath", "");
|
||||||
};
|
}
|
||||||
ElementPropHeaderManager.prototype = new HashTree();
|
}
|
||||||
|
|
||||||
let CollectionPropHeaderManagerHeaders = function () {
|
export class ThreadGroup extends DefaultTestElement {
|
||||||
HashTree.call(this, "collectionProp", {
|
constructor(testName) {
|
||||||
name: "HeaderManager.headers"
|
super('ThreadGroup', 'ThreadGroupGui', 'ThreadGroup', testName);
|
||||||
});
|
|
||||||
};
|
|
||||||
CollectionPropHeaderManagerHeaders.prototype = new HashTree();
|
|
||||||
|
|
||||||
|
this.intProp("ThreadGroup.num_threads", 1);
|
||||||
|
this.intProp("ThreadGroup.ramp_time", 1);
|
||||||
|
this.longProp("ThreadGroup.delay", 0);
|
||||||
|
this.longProp("ThreadGroup.duration", 0);
|
||||||
|
this.stringProp("ThreadGroup.on_sample_error", "continue");
|
||||||
|
this.boolProp("ThreadGroup.scheduler", false);
|
||||||
|
|
||||||
let elementPropHeaderManager = function (name, value) {
|
let loopAttrs = {
|
||||||
let ht = new HashTree("elementProp", {
|
name: "ThreadGroup.main_controller",
|
||||||
name: name,
|
elementType: "LoopController",
|
||||||
elementType: "Header"
|
guiclass: "LoopControlPanel",
|
||||||
});
|
testclass: "LoopController",
|
||||||
ht.stringProp("Header.name", name);
|
testname: "Loop Controller",
|
||||||
ht.stringProp("Header.value", value);
|
enabled: "true"
|
||||||
return ht;
|
};
|
||||||
};
|
let loopController = this.add(new Element('elementProp', loopAttrs));
|
||||||
|
loopController.boolProp('LoopController.continue_forever', false);
|
||||||
|
loopController.stringProp('LoopController.loops', 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default Jmx;
|
export class HTTPSamplerProxy extends DefaultTestElement {
|
||||||
|
constructor(testName, request) {
|
||||||
|
super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName || 'HTTP Request');
|
||||||
|
this.request = request || {};
|
||||||
|
|
||||||
|
this.stringProp("HTTPSampler.domain", this.request.hostname);
|
||||||
|
this.stringProp("HTTPSampler.protocol", this.request.protocol.split(":")[0]);
|
||||||
|
this.stringProp("HTTPSampler.path", this.request.pathname);
|
||||||
|
this.stringProp("HTTPSampler.method", this.request.method);
|
||||||
|
if (this.request.port) {
|
||||||
|
this.stringProp("HTTPSampler.port", "");
|
||||||
|
} else {
|
||||||
|
this.stringProp("HTTPSampler.port", this.request.port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequestArguments(arg) {
|
||||||
|
if (arg instanceof HTTPSamplerArguments) {
|
||||||
|
this.add(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequestBody(body) {
|
||||||
|
if (body instanceof HTTPSamplerArguments) {
|
||||||
|
this.boolProp('HTTPSampler.postBodyRaw', true);
|
||||||
|
this.add(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
putRequestHeader(header) {
|
||||||
|
if (header instanceof HeaderManager) {
|
||||||
|
this.put(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
putResponseAssertion(assertion) {
|
||||||
|
if (assertion instanceof ResponseAssertion) {
|
||||||
|
this.put(assertion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
putDurationAssertion(assertion) {
|
||||||
|
if (assertion instanceof DurationAssertion) {
|
||||||
|
this.put(assertion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这是一个Element
|
||||||
|
export class HTTPSamplerArguments extends Element {
|
||||||
|
constructor(args) {
|
||||||
|
super('elementProp', {
|
||||||
|
name: "HTTPsampler.Arguments", // s必须小写
|
||||||
|
elementType: "Arguments",
|
||||||
|
guiclass: "HTTPArgumentsPanel",
|
||||||
|
testclass: "Arguments",
|
||||||
|
enabled: "true"
|
||||||
|
});
|
||||||
|
|
||||||
|
this.args = args || [];
|
||||||
|
|
||||||
|
let collectionProp = this.collectionProp('Arguments.arguments');
|
||||||
|
this.args.forEach(arg => {
|
||||||
|
let elementProp = collectionProp.elementProp(arg.name, 'HTTPArgument');
|
||||||
|
elementProp.boolProp('HTTPArgument.always_encode', false);
|
||||||
|
elementProp.boolProp('HTTPArgument.use_equals', true);
|
||||||
|
if (arg.name) {
|
||||||
|
elementProp.stringProp('Argument.name', arg.name);
|
||||||
|
}
|
||||||
|
elementProp.stringProp('Argument.value', arg.value);
|
||||||
|
elementProp.stringProp('Argument.metadata', "=");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DurationAssertion extends DefaultTestElement {
|
||||||
|
constructor(testName, duration) {
|
||||||
|
super('DurationAssertion', 'DurationAssertionGui', 'DurationAssertion', testName || 'Duration Assertion');
|
||||||
|
this.duration = duration || 0;
|
||||||
|
this.stringProp('DurationAssertion.duration', this.duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ResponseAssertion extends DefaultTestElement {
|
||||||
|
constructor(testName, assertion) {
|
||||||
|
super('ResponseAssertion', 'AssertionGui', 'ResponseAssertion', testName || 'Response Assertion');
|
||||||
|
this.assertion = assertion || {};
|
||||||
|
|
||||||
|
this.stringProp('Assertion.test_field', this.assertion.field);
|
||||||
|
this.boolProp('Assertion.assume_success', false);
|
||||||
|
this.intProp('Assertion.test_type', this.assertion.type);
|
||||||
|
this.stringProp('Assertion.custom_message', this.assertion.message);
|
||||||
|
|
||||||
|
let collectionProp = this.collectionProp('Asserion.test_strings');
|
||||||
|
let random = Math.floor(Math.random() * 10000);
|
||||||
|
collectionProp.stringProp(random, this.assertion.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ResponseCodeAssertion extends ResponseAssertion {
|
||||||
|
constructor(testName, type, value, message) {
|
||||||
|
let assertion = {
|
||||||
|
field: 'Assertion.response_code',
|
||||||
|
type: type,
|
||||||
|
value: value,
|
||||||
|
message: message,
|
||||||
|
}
|
||||||
|
super(testName, assertion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ResponseDataAssertion extends ResponseAssertion {
|
||||||
|
constructor(testName, type, value, message) {
|
||||||
|
let assertion = {
|
||||||
|
field: 'Assertion.response_data',
|
||||||
|
type: type,
|
||||||
|
value: value,
|
||||||
|
message: message,
|
||||||
|
}
|
||||||
|
super(testName, assertion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ResponseHeadersAssertion extends ResponseAssertion {
|
||||||
|
constructor(testName, type, value, message) {
|
||||||
|
let assertion = {
|
||||||
|
field: 'Assertion.response_headers',
|
||||||
|
type: type,
|
||||||
|
value: value,
|
||||||
|
message: message,
|
||||||
|
}
|
||||||
|
super(testName, assertion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HeaderManager extends DefaultTestElement {
|
||||||
|
constructor(testName, headers) {
|
||||||
|
super('HeaderManager', 'HeaderPanel', 'HeaderManager', testName || 'HTTP Header manager');
|
||||||
|
this.headers = headers || [];
|
||||||
|
|
||||||
|
let collectionProp = this.collectionProp('HeaderManager.headers');
|
||||||
|
this.headers.forEach(header => {
|
||||||
|
let elementProp = collectionProp.elementProp('', 'Header');
|
||||||
|
elementProp.stringProp('Header.name', header.name);
|
||||||
|
elementProp.stringProp('Header.value', header.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Arguments extends DefaultTestElement {
|
||||||
|
constructor(testName, args) {
|
||||||
|
super('Arguments', 'ArgumentsPanel', 'Arguments', testName || 'User Defined Variables');
|
||||||
|
this.args = args || [];
|
||||||
|
|
||||||
|
let collectionProp = this.collectionProp('Arguments.arguments');
|
||||||
|
this.args.forEach(arg => {
|
||||||
|
let elementProp = collectionProp.elementProp(arg.name, 'HTTPArgument');
|
||||||
|
elementProp.boolProp('HTTPArgument.always_encode', true);
|
||||||
|
elementProp.stringProp('Argument.name', arg.name);
|
||||||
|
elementProp.stringProp('Argument.value', arg.value);
|
||||||
|
elementProp.stringProp('Argument.metadata', "=");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,40 @@
|
||||||
import {generateId} from "element-ui/src/utils/util";
|
import {
|
||||||
|
Element,
|
||||||
|
HTTPSamplerProxy,
|
||||||
|
HashTree,
|
||||||
|
TestElement,
|
||||||
|
TestPlan,
|
||||||
|
ThreadGroup,
|
||||||
|
HeaderManager,
|
||||||
|
HTTPSamplerArguments,
|
||||||
|
ResponseCodeAssertion,
|
||||||
|
ResponseDataAssertion,
|
||||||
|
ResponseHeadersAssertion
|
||||||
|
} from "./JMX";
|
||||||
|
|
||||||
|
export const generateId = function () {
|
||||||
|
return Math.floor(Math.random() * 10000);
|
||||||
|
};
|
||||||
|
|
||||||
export const BODY_TYPE = {
|
export const BODY_TYPE = {
|
||||||
KV: "KV",
|
KV: "KeyValue",
|
||||||
TEXT: "TEXT"
|
FORM_DATA: "Form Data",
|
||||||
|
RAW: "Raw"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ASSERTION_TYPE = {
|
export const ASSERTION_TYPE = {
|
||||||
TEXT: "TEXT",
|
TEXT: "Text",
|
||||||
REGEX: "REGEX",
|
REGEX: "Regex",
|
||||||
RESPONSE_TIME: "RESPONSE_TIME"
|
RESPONSE_TIME: "Response Time"
|
||||||
}
|
}
|
||||||
|
|
||||||
class BaseConfig {
|
export const ASSERTION_REGEX_SUBJECT = {
|
||||||
|
RESPONSE_CODE: "Response Code",
|
||||||
|
RESPONSE_HEADERS: "Response Headers",
|
||||||
|
RESPONSE_DATA: "Response Data"
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BaseConfig {
|
||||||
|
|
||||||
set(options) {
|
set(options) {
|
||||||
options = this.initOptions(options)
|
options = this.initOptions(options)
|
||||||
|
@ -41,6 +64,10 @@ class BaseConfig {
|
||||||
initOptions(options) {
|
initOptions(options) {
|
||||||
return options || {};
|
return options || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Test extends BaseConfig {
|
export class Test extends BaseConfig {
|
||||||
|
@ -60,19 +87,27 @@ export class Test extends BaseConfig {
|
||||||
options.scenarioDefinition = options.scenarioDefinition || [new Scenario()];
|
options.scenarioDefinition = options.scenarioDefinition || [new Scenario()];
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toJMX() {
|
||||||
|
return {
|
||||||
|
name: this.name + '.jmx',
|
||||||
|
xml: new JMXGenerator(this).toXML()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Scenario extends BaseConfig {
|
export class Scenario extends BaseConfig {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super();
|
super();
|
||||||
|
this.id = generateId();
|
||||||
this.name = null;
|
this.name = null;
|
||||||
this.url = null;
|
this.url = null;
|
||||||
this.variables = [];
|
this.parameters = [];
|
||||||
this.headers = [];
|
this.headers = [];
|
||||||
this.requests = [];
|
this.requests = [];
|
||||||
|
|
||||||
this.set(options);
|
this.set(options);
|
||||||
this.sets({variables: KeyValue, headers: KeyValue, requests: Request}, options);
|
this.sets({parameters: KeyValue, headers: KeyValue, requests: Request}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
initOptions(options) {
|
initOptions(options) {
|
||||||
|
@ -85,7 +120,7 @@ export class Scenario extends BaseConfig {
|
||||||
export class Request extends BaseConfig {
|
export class Request extends BaseConfig {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super();
|
super();
|
||||||
this.randomId = generateId();
|
this.id = generateId();
|
||||||
this.name = null;
|
this.name = null;
|
||||||
this.url = null;
|
this.url = null;
|
||||||
this.method = null;
|
this.method = null;
|
||||||
|
@ -113,26 +148,50 @@ export class Body extends BaseConfig {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super();
|
super();
|
||||||
this.type = null;
|
this.type = null;
|
||||||
this.text = null;
|
this.raw = null;
|
||||||
this.kvs = [];
|
this.kvs = [];
|
||||||
|
|
||||||
this.set(options);
|
this.set(options);
|
||||||
this.sets({kvs: KeyValue}, options);
|
this.sets({kvs: KeyValue}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
if (this.isKV()) {
|
||||||
|
return this.kvs.some(kv => {
|
||||||
|
return kv.isValid();
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return !!this.raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isKV() {
|
isKV() {
|
||||||
return this.type === BODY_TYPE.KV;
|
return this.type === BODY_TYPE.KV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeyValue extends BaseConfig {
|
export class KeyValue extends BaseConfig {
|
||||||
constructor(options) {
|
constructor() {
|
||||||
|
let options, key, value;
|
||||||
|
if (arguments.length === 1) {
|
||||||
|
options = arguments[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.length === 2) {
|
||||||
|
key = arguments[0];
|
||||||
|
value = arguments[1];
|
||||||
|
}
|
||||||
|
|
||||||
super();
|
super();
|
||||||
this.key = null;
|
this.name = key;
|
||||||
this.value = null;
|
this.value = value;
|
||||||
|
|
||||||
this.set(options);
|
this.set(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return !!this.name || !!this.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Assertions extends BaseConfig {
|
export class Assertions extends BaseConfig {
|
||||||
|
@ -140,20 +199,20 @@ export class Assertions extends BaseConfig {
|
||||||
super();
|
super();
|
||||||
this.text = [];
|
this.text = [];
|
||||||
this.regex = [];
|
this.regex = [];
|
||||||
this.responseTime = null;
|
this.duration = null;
|
||||||
|
|
||||||
this.set(options);
|
this.set(options);
|
||||||
this.sets({text: KeyValue, regex: KeyValue}, options);
|
this.sets({text: Text, regex: Regex}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
initOptions(options) {
|
initOptions(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.responseTime = new ResponseTime(options.responseTime);
|
options.duration = new ResponseTime(options.duration);
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AssertionType extends BaseConfig {
|
export class AssertionType extends BaseConfig {
|
||||||
constructor(type) {
|
constructor(type) {
|
||||||
super();
|
super();
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -180,14 +239,155 @@ export class Regex extends AssertionType {
|
||||||
|
|
||||||
this.set(options);
|
this.set(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return !!this.subject && !!this.expression;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResponseTime extends AssertionType {
|
export class ResponseTime extends AssertionType {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(ASSERTION_TYPE.RESPONSE_TIME);
|
super(ASSERTION_TYPE.RESPONSE_TIME);
|
||||||
this.responseInTime = null;
|
this.value = null;
|
||||||
|
|
||||||
this.set(options);
|
this.set(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return !!this.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** ------------------------------------ **/
|
||||||
|
const JMX_ASSERTION_CONDITION = {
|
||||||
|
MATCH: 1,
|
||||||
|
CONTAINS: 1 << 1,
|
||||||
|
NOT: 1 << 2,
|
||||||
|
EQUALS: 1 << 3,
|
||||||
|
SUBSTRING: 1 << 4,
|
||||||
|
OR: 1 << 5
|
||||||
|
}
|
||||||
|
|
||||||
|
class JMXRequest {
|
||||||
|
constructor(request) {
|
||||||
|
if (request && request instanceof Request && request.url) {
|
||||||
|
let url = new URL(request.url);
|
||||||
|
this.method = request.method;
|
||||||
|
this.hostname = url.hostname;
|
||||||
|
this.pathname = url.pathname;
|
||||||
|
this.port = url.port;
|
||||||
|
this.protocol = url.protocol.split(":")[0];
|
||||||
|
if (this.method.toUpperCase() !== "GET") {
|
||||||
|
this.pathname += url.search.replace('&', '&');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JMeterTestPlan extends Element {
|
||||||
|
constructor() {
|
||||||
|
super('jmeterTestPlan', {
|
||||||
|
version: "1.2", properties: "5.0", jmeter: "5.2.1"
|
||||||
|
});
|
||||||
|
|
||||||
|
this.add(new HashTree());
|
||||||
|
}
|
||||||
|
|
||||||
|
put(te) {
|
||||||
|
if (te instanceof TestElement) {
|
||||||
|
this.elements[0].add(te);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JMXGenerator {
|
||||||
|
constructor(test) {
|
||||||
|
if (!test || !(test instanceof Test)) return;
|
||||||
|
|
||||||
|
let testPlan = new TestPlan(test.name);
|
||||||
|
test.scenarioDefinition.forEach(scenario => {
|
||||||
|
let threadGroup = new ThreadGroup(scenario.name);
|
||||||
|
scenario.requests.forEach(request => {
|
||||||
|
let httpSamplerProxy = new HTTPSamplerProxy(request.name, new JMXRequest(request));
|
||||||
|
|
||||||
|
this.addRequestHeader(httpSamplerProxy, request);
|
||||||
|
|
||||||
|
if (request.method.toUpperCase() === 'GET') {
|
||||||
|
this.addRequestArguments(httpSamplerProxy, request);
|
||||||
|
} else {
|
||||||
|
this.addRequestBody(httpSamplerProxy, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addRequestAssertion(httpSamplerProxy, request);
|
||||||
|
|
||||||
|
threadGroup.put(httpSamplerProxy);
|
||||||
|
})
|
||||||
|
|
||||||
|
testPlan.put(threadGroup);
|
||||||
|
})
|
||||||
|
|
||||||
|
this.jmeterTestPlan = new JMeterTestPlan();
|
||||||
|
this.jmeterTestPlan.put(testPlan);
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequestHeader(httpSamplerProxy, request) {
|
||||||
|
let name = request.name + " Headers";
|
||||||
|
let headers = request.headers.filter(this.filter);
|
||||||
|
httpSamplerProxy.putRequestHeader(new HeaderManager(name, headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequestArguments(httpSamplerProxy, request) {
|
||||||
|
let args = request.parameters.filter(this.filter)
|
||||||
|
httpSamplerProxy.addRequestArguments(new HTTPSamplerArguments(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequestBody(httpSamplerProxy, request) {
|
||||||
|
let body = [];
|
||||||
|
if (request.body.isKV()) {
|
||||||
|
body = request.body.kvs.filter(this.filter);
|
||||||
|
} else {
|
||||||
|
body.push({name: '', value: request.body.raw});
|
||||||
|
}
|
||||||
|
|
||||||
|
httpSamplerProxy.addRequestBody(new HTTPSamplerArguments(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
addRequestAssertion(httpSamplerProxy, request) {
|
||||||
|
let assertions = request.assertions;
|
||||||
|
if (assertions.regex.length > 0) {
|
||||||
|
assertions.regex.filter(this.filter).forEach(regex => {
|
||||||
|
httpSamplerProxy.putResponseAssertion(this.getAssertion(regex));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assertions.duration.isValid()) {
|
||||||
|
httpSamplerProxy.putDurationAssertion(assertions.duration.type, assertions.duration.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAssertion(regex) {
|
||||||
|
let name = regex.description;
|
||||||
|
let type = JMX_ASSERTION_CONDITION.MATCH; // 固定用Match,自己写正则
|
||||||
|
let value = regex.expression;
|
||||||
|
switch (regex.subject) {
|
||||||
|
case ASSERTION_REGEX_SUBJECT.RESPONSE_CODE:
|
||||||
|
return new ResponseCodeAssertion(name, type, value);
|
||||||
|
case ASSERTION_REGEX_SUBJECT.RESPONSE_DATA:
|
||||||
|
return new ResponseDataAssertion(name, type, value);
|
||||||
|
case ASSERTION_REGEX_SUBJECT.RESPONSE_HEADERS:
|
||||||
|
return new ResponseHeadersAssertion(name, type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filter(config) {
|
||||||
|
return config.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
toXML() {
|
||||||
|
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
||||||
|
xml += this.jmeterTestPlan.toXML();
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
export const get = [{
|
|
||||||
"headers": [{"name": "Upgrade-Insecure-Requests", "value": "1"}, {
|
|
||||||
"name": "User-Agent",
|
|
||||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"
|
|
||||||
}, {
|
|
||||||
"name": "Accept",
|
|
||||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
|
||||||
}],
|
|
||||||
"label": "https://www.baidu.com/",
|
|
||||||
"method": "GET",
|
|
||||||
"requestId": "21478",
|
|
||||||
"request_subtype": "",
|
|
||||||
"request_type": "top_level",
|
|
||||||
"tabId": 394,
|
|
||||||
"timestamp": 1587958164590,
|
|
||||||
"url": "https://www.baidu.com/"
|
|
||||||
}, {
|
|
||||||
"headers": [{"name": "Accept", "value": "application/json, text/javascript, */*; q=0.01"}, {
|
|
||||||
"name": "User-Agent",
|
|
||||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"
|
|
||||||
}],
|
|
||||||
"label": "https://www.baidu.com/sugrec?prod=pc_his&from=pc_web&json=1&sid=1447_31124_21122_31424_31341_30903_31229_30824_26350_31164_31195&hisdata=&csor=0",
|
|
||||||
"method": "GET",
|
|
||||||
"requestId": "21522",
|
|
||||||
"request_subtype": "",
|
|
||||||
"request_type": "ajax",
|
|
||||||
"tabId": 394,
|
|
||||||
"timestamp": 1587958165726,
|
|
||||||
"url": "https://www.baidu.com/sugrec?prod=pc_his&from=pc_web&json=1&sid=1447_31124_21122_31424_31341_30903_31229_30824_26350_31164_31195&hisdata=&csor=0"
|
|
||||||
}, {
|
|
||||||
"headers": [{"name": "Accept", "value": "text/plain, */*; q=0.01"}, {
|
|
||||||
"name": "User-Agent",
|
|
||||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"
|
|
||||||
}, {"name": "X-Requested-With", "value": "XMLHttpRequest"}],
|
|
||||||
"label": "https://www.baidu.com/home/xman/data/tipspluslist?indextype=manht&_req_seqid=0xacf4e73f00017936&asyn=1&t=1587958166015&sid=1447_31124_21122_31424_31341_30903_31229_30824_26350_31164_31195",
|
|
||||||
"method": "GET",
|
|
||||||
"requestId": "21540",
|
|
||||||
"request_subtype": "",
|
|
||||||
"request_type": "ajax",
|
|
||||||
"tabId": 394,
|
|
||||||
"timestamp": 1587958166018,
|
|
||||||
"url": "https://www.baidu.com/home/xman/data/tipspluslist?indextype=manht&_req_seqid=0xacf4e73f00017936&asyn=1&t=1587958166015&sid=1447_31124_21122_31424_31341_30903_31229_30824_26350_31164_31195"
|
|
||||||
}, {
|
|
||||||
"headers": [{"name": "Accept", "value": "text/plain, */*; q=0.01"}, {
|
|
||||||
"name": "User-Agent",
|
|
||||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"
|
|
||||||
}, {"name": "X-Requested-With", "value": "XMLHttpRequest"}],
|
|
||||||
"label": "https://www.baidu.com/home/msg/data/personalcontent?num=8&indextype=manht&_req_seqid=0xacf4e73f00017936&asyn=1&t=1587958166028&sid=1447_31124_21122_31424_31341_30903_31229_30824_26350_31164_31195",
|
|
||||||
"method": "GET",
|
|
||||||
"requestId": "21541",
|
|
||||||
"request_subtype": "",
|
|
||||||
"request_type": "ajax",
|
|
||||||
"tabId": 394,
|
|
||||||
"timestamp": 1587958166031,
|
|
||||||
"url": "https://www.baidu.com/home/msg/data/personalcontent?num=8&indextype=manht&_req_seqid=0xacf4e73f00017936&asyn=1&t=1587958166028&sid=1447_31124_21122_31424_31341_30903_31229_30824_26350_31164_31195"
|
|
||||||
}];
|
|
||||||
|
|
||||||
export const post = [{
|
|
||||||
"body": ["{\"id\":null,\"projectId\":\"e15d2d02-7404-485f-944b-2f2dbd456e3e\",\"name\":\"test second\",\"scenarioDefinition\":\"[{\\\"name\\\":null,\\\"url\\\":null,\\\"variables\\\":[],\\\"headers\\\":[],\\\"requests\\\":[{\\\"randomId\\\":2311,\\\"name\\\":null,\\\"url\\\":null,\\\"method\\\":\\\"GET\\\",\\\"parameters\\\":[],\\\"headers\\\":[],\\\"body\\\":{\\\"type\\\":null,\\\"text\\\":null,\\\"kvs\\\":[]},\\\"assertions\\\":{\\\"text\\\":[],\\\"regex\\\":[],\\\"responseTime\\\":{\\\"type\\\":\\\"RESPONSE_TIME\\\",\\\"responseInTime\\\":null}},\\\"extract\\\":[]}]}]\"}"],
|
|
||||||
"headers": [{"name": "Accept", "value": "application/json, text/plain, */*"}, {
|
|
||||||
"name": "User-Agent",
|
|
||||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"
|
|
||||||
}, {"name": "Content-Type", "value": "application/json;charset=UTF-8"}],
|
|
||||||
"label": "http://localhost:8080/api/save",
|
|
||||||
"method": "POST",
|
|
||||||
"requestId": "26129",
|
|
||||||
"request_subtype": "",
|
|
||||||
"request_type": "ajax",
|
|
||||||
"tabId": 466,
|
|
||||||
"timestamp": 1587968228917,
|
|
||||||
"url": "http://localhost:8080/api/save"
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
export const scenario = [{
|
|
||||||
"name": "Scenario 1",
|
|
||||||
"url": null,
|
|
||||||
"variables": [],
|
|
||||||
"headers": [],
|
|
||||||
"requests": [{
|
|
||||||
"randomId": 6271,
|
|
||||||
"name": "Request 1",
|
|
||||||
"url": "https://www.baidu.com",
|
|
||||||
"method": "GET",
|
|
||||||
"parameters": [{"key": "flag", "value": "test"}],
|
|
||||||
"headers": [{"key": "test_header", "value": "test_heade_value"}],
|
|
||||||
"body": {"type": null, "text": null, "kvs": []},
|
|
||||||
"assertions": {
|
|
||||||
"text": [],
|
|
||||||
"regex": [{"type": "REGEX", "subject": "HTTP_CODE", "expression": "^200$", "description": "equals: 200"}],
|
|
||||||
"responseTime": {"type": "RESPONSE_TIME", "responseInTime": null}
|
|
||||||
},
|
|
||||||
"extract": []
|
|
||||||
}]
|
|
||||||
}]
|
|
|
@ -184,8 +184,9 @@ export default {
|
||||||
name: "场景名称",
|
name: "场景名称",
|
||||||
base_url: "基础URL",
|
base_url: "基础URL",
|
||||||
base_url_description: "基础URL作为所有请求的URL前缀",
|
base_url_description: "基础URL作为所有请求的URL前缀",
|
||||||
variables: "变量",
|
parameters: "请求变量",
|
||||||
headers: "请求头"
|
headers: "请求头",
|
||||||
|
kv_description: "将用于未设置该项的请求",
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
input_name: "请输入请求名称",
|
input_name: "请输入请求名称",
|
||||||
|
|
Loading…
Reference in New Issue