This commit is contained in:
chenjianxing 2020-06-15 19:00:42 +08:00
commit 1e99e83ea4
24 changed files with 226 additions and 36 deletions

View File

@ -1,8 +1,11 @@
package io.metersphere.api.dto;
import io.metersphere.api.dto.scenario.Scenario;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Setter
@Getter
public class SaveAPITestRequest {
@ -13,5 +16,5 @@ public class SaveAPITestRequest {
private String name;
private String scenarioDefinition;
private List<Scenario> scenarioDefinition;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.api.dto.scenario;
import lombok.Data;
import java.util.List;
@Data
public class Body {
private String type;
private String raw;
private List<KeyValue> kvs;
}

View File

@ -0,0 +1,9 @@
package io.metersphere.api.dto.scenario;
import lombok.Data;
@Data
public class KeyValue {
private String name;
private String value;
}

View File

@ -0,0 +1,19 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.dto.scenario.assertions.Assertions;
import io.metersphere.api.dto.scenario.extract.Extract;
import lombok.Data;
import java.util.List;
@Data
public class Request {
private String name;
private String url;
private String method;
private List<KeyValue> parameters;
private List<KeyValue> headers;
private Body body;
private Assertions assertions;
private Extract extract;
}

View File

@ -0,0 +1,14 @@
package io.metersphere.api.dto.scenario;
import lombok.Data;
import java.util.List;
@Data
public class Scenario {
private String name;
private String url;
private List<KeyValue> variables;
private List<KeyValue> headers;
private List<Request> requests;
}

View File

@ -0,0 +1,14 @@
package io.metersphere.api.dto.scenario.assertions;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class AssertionDuration extends AssertionType {
private long value;
public AssertionDuration() {
setType(AssertionType.DURATION);
}
}

View File

@ -0,0 +1,16 @@
package io.metersphere.api.dto.scenario.assertions;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class AssertionRegex extends AssertionType {
private String subject;
private String expression;
private String description;
public AssertionRegex() {
setType(AssertionType.REGEX);
}
}

View File

@ -0,0 +1,12 @@
package io.metersphere.api.dto.scenario.assertions;
import lombok.Data;
@Data
public class AssertionType {
public final static String REGEX = "Regex";
public final static String DURATION = "Duration";
public final static String TEXT = "Text";
private String type;
}

View File

@ -0,0 +1,11 @@
package io.metersphere.api.dto.scenario.assertions;
import lombok.Data;
import java.util.List;
@Data
public class Assertions {
private List<AssertionRegex> regex;
private AssertionDuration duration;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.api.dto.scenario.extract;
import lombok.Data;
import java.util.List;
@Data
public class Extract {
private List<ExtractRegex> regex;
private List<ExtractJSONPath> json;
private List<ExtractXPath> xpath;
}

View File

@ -0,0 +1,13 @@
package io.metersphere.api.dto.scenario.extract;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ExtractCommon extends ExtractType {
private String variable;
private String value; // value: ${variable}
private String expression;
private String description;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.api.dto.scenario.extract;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ExtractJSONPath extends ExtractCommon {
public ExtractJSONPath() {
setType(ExtractType.JSON_PATH);
}
}

View File

@ -0,0 +1,12 @@
package io.metersphere.api.dto.scenario.extract;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ExtractRegex extends ExtractCommon {
public ExtractRegex() {
setType(ExtractType.REGEX);
}
}

View File

@ -0,0 +1,12 @@
package io.metersphere.api.dto.scenario.extract;
import lombok.Data;
@Data
public class ExtractType {
public final static String REGEX = "Regex";
public final static String JSON_PATH = "JSONPath";
public final static String XPATH = "XPath";
private String type;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.api.dto.scenario.extract;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ExtractXPath extends ExtractCommon {
public ExtractXPath() {
setType(ExtractType.XPATH);
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.api.service;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.APITestResult;
import io.metersphere.api.dto.QueryAPITestRequest;
import io.metersphere.api.dto.SaveAPITestRequest;
@ -25,6 +26,7 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;
@ -73,10 +75,17 @@ public class APITestService {
}
public void copy(SaveAPITestRequest request) {
request.setName(request.getName() + " Copy");
try {
checkNameExist(request);
} catch (Exception e) {
request.setName(request.getName() + " " + new Random().nextInt(1000));
}
// copy test
ApiTestWithBLOBs copy = get(request.getId());
copy.setId(UUID.randomUUID().toString());
copy.setName(copy.getName() + " Copy");
copy.setName(request.getName());
copy.setCreateTime(System.currentTimeMillis());
copy.setUpdateTime(System.currentTimeMillis());
copy.setStatus(APITestStatus.Saved.name());
@ -96,6 +105,10 @@ public class APITestService {
return apiTestMapper.selectByPrimaryKey(id);
}
public List<ApiTest> getApiTestByProjectId(String projectId) {
return extApiTestMapper.getApiTestByProjectId(projectId);
}
public void delete(String testId) {
deleteFileByTestId(testId);
apiReportService.deleteByTestId(testId);
@ -138,7 +151,7 @@ public class APITestService {
test.setId(request.getId());
test.setName(request.getName());
test.setProjectId(request.getProjectId());
test.setScenarioDefinition(request.getScenarioDefinition());
test.setScenarioDefinition(JSONObject.toJSONString(request.getScenarioDefinition()));
test.setUpdateTime(System.currentTimeMillis());
test.setStatus(APITestStatus.Saved.name());
apiTestMapper.updateByPrimaryKeySelective(test);
@ -151,7 +164,7 @@ public class APITestService {
test.setId(request.getId());
test.setName(request.getName());
test.setProjectId(request.getProjectId());
test.setScenarioDefinition(request.getScenarioDefinition());
test.setScenarioDefinition(JSONObject.toJSONString(request.getScenarioDefinition()));
test.setCreateTime(System.currentTimeMillis());
test.setUpdateTime(System.currentTimeMillis());
test.setStatus(APITestStatus.Saved.name());
@ -194,7 +207,4 @@ public class APITestService {
}
}
public List<ApiTest> getApiTestByProjectId(String projectId) {
return extApiTestMapper.getApiTestByProjectId(projectId);
}
}

View File

@ -5,7 +5,8 @@
<el-container class="test-container" v-loading="result.loading">
<el-header>
<el-row type="flex" align="middle">
<el-input :disabled="isReadOnly" class="test-name" v-model="test.name" maxlength="60" :placeholder="$t('api_test.input_name')"
<el-input :disabled="isReadOnly" class="test-name" v-model="test.name" maxlength="60"
:placeholder="$t('api_test.input_name')"
show-word-limit>
<el-select :disabled="isReadOnly" class="test-project" v-model="test.projectId" slot="prepend"
:placeholder="$t('api_test.select_project')">
@ -17,7 +18,8 @@
{{$t('commons.save')}}
</el-button>
<el-button type="primary" plain v-if="!isShowRun" :disabled="isDisabled || isReadOnly" @click="saveRunTest">
<el-button type="primary" plain v-if="!isShowRun" :disabled="isDisabled || isReadOnly"
@click="saveRunTest">
{{$t('load_test.save_and_run')}}
</el-button>
@ -25,7 +27,8 @@
{{$t('api_test.run')}}
</el-button>
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}
</el-button>
<el-dropdown trigger="click" @command="handleCommand">
<el-button class="el-dropdown-link more" icon="el-icon-more" plain/>
@ -165,13 +168,7 @@
},
getOptions(url) {
let formData = new FormData();
let request = {
id: this.test.id,
projectId: this.test.projectId,
name: this.test.name,
scenarioDefinition: JSON.stringify(this.test.scenarioDefinition)
}
let requestJson = JSON.stringify(request);
let requestJson = JSON.stringify(this.test);
formData.append('request', new Blob([requestJson], {
type: "application/json"

View File

@ -143,7 +143,7 @@
});
},
handleCopy(test) {
this.result = this.$post("/api/copy", {id: test.id}, () => {
this.result = this.$post("/api/copy", {projectId: test.projectId, id: test.id, name: test.name}, () => {
this.$success(this.$t('commons.copy_success'));
this.search();
});

View File

@ -15,13 +15,13 @@
<script>
import {ResponseTime} from "../../model/ScenarioModel";
import {Duration} from "../../model/ScenarioModel";
export default {
name: "MsApiAssertionResponseTime",
name: "MsApiAssertionDuration",
props: {
duration: ResponseTime,
duration: Duration,
value: [Number, String],
edit: Boolean,
callback: Function,

View File

@ -6,14 +6,14 @@
size="small">
<el-option :label="$t('api_test.request.assertions.text')" :value="options.TEXT"/>
<el-option :label="$t('api_test.request.assertions.regex')" :value="options.REGEX"/>
<el-option :label="$t('api_test.request.assertions.response_time')" :value="options.RESPONSE_TIME"/>
<el-option :label="$t('api_test.request.assertions.response_time')" :value="options.DURATION"/>
</el-select>
</el-col>
<el-col :span="20">
<ms-api-assertion-text :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.TEXT" :callback="after"/>
<ms-api-assertion-regex :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.REGEX" :callback="after"/>
<ms-api-assertion-response-time :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
v-if="type === options.RESPONSE_TIME" :callback="after"/>
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
v-if="type === options.DURATION" :callback="after"/>
</el-col>
</el-row>
@ -24,14 +24,14 @@
<script>
import MsApiAssertionText from "./ApiAssertionText";
import MsApiAssertionRegex from "./ApiAssertionRegex";
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
import {ASSERTION_TYPE, Assertions, ResponseTime} from "../../model/ScenarioModel";
import MsApiAssertionDuration from "./ApiAssertionDuration";
import {ASSERTION_TYPE, Assertions} from "../../model/ScenarioModel";
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
export default {
name: "MsApiAssertions",
components: {MsApiAssertionsEdit, MsApiAssertionResponseTime, MsApiAssertionRegex, MsApiAssertionText},
components: {MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText},
props: {
assertions: Assertions,

View File

@ -13,7 +13,7 @@
<div>
{{$t("api_test.request.assertions.response_time")}}
</div>
<ms-api-assertion-response-time :is-read-only="isReadOnly" v-model="assertions.duration.value" :duration="assertions.duration" :edit="true"/>
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="assertions.duration.value" :duration="assertions.duration" :edit="true"/>
</div>
</div>
@ -21,13 +21,13 @@
<script>
import MsApiAssertionRegex from "./ApiAssertionRegex";
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
import MsApiAssertionDuration from "./ApiAssertionDuration";
import {Assertions} from "../../model/ScenarioModel";
export default {
name: "MsApiAssertionsEdit",
components: {MsApiAssertionResponseTime, MsApiAssertionRegex},
components: {MsApiAssertionDuration, MsApiAssertionRegex},
props: {
assertions: Assertions,

View File

@ -40,7 +40,7 @@ export const BODY_TYPE = {
export const ASSERTION_TYPE = {
TEXT: "Text",
REGEX: "Regex",
RESPONSE_TIME: "Response Time"
DURATION: "Duration"
}
export const ASSERTION_REGEX_SUBJECT = {
@ -253,7 +253,7 @@ export class Assertions extends BaseConfig {
initOptions(options) {
options = options || {};
options.duration = new ResponseTime(options.duration);
options.duration = new Duration(options.duration);
return options;
}
}
@ -291,9 +291,9 @@ export class Regex extends AssertionType {
}
}
export class ResponseTime extends AssertionType {
export class Duration extends AssertionType {
constructor(options) {
super(ASSERTION_TYPE.RESPONSE_TIME);
super(ASSERTION_TYPE.DURATION);
this.value = undefined;
this.set(options);

View File

@ -61,7 +61,7 @@
<el-input v-model="form.name" autocomplete="off"/>
</el-form-item>
<el-form-item :label="$t('commons.description')" prop="description">
<el-input v-model="form.description" autocomplete="off"/>
<el-input v-model="form.description" autocomplete="off" type="textarea"/>
</el-form-item>
</el-form>
<template v-slot:footer>

View File

@ -61,7 +61,7 @@
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"/>
</el-form-item>
<el-form-item :label="$t('commons.description')">
<el-form-item :label="$t('commons.description')" prop="description">
<el-input type="textarea" v-model="form.description"></el-input>
</el-form-item>
<el-form-item :label="$t('workspace.organization_name')" prop="organizationId">