fix(接口测试): 修复引用的case无法添加断言的缺陷

--bug=1027145 --user=王孝刚 【接口测试】场景详情-引用的场景和case-无法添加断言
https://www.tapd.cn/55049933/s/1383491
This commit is contained in:
wxg0103 2023-06-20 15:22:39 +08:00 committed by 刘瑞斌
parent 25b515c16a
commit f3df278755
12 changed files with 145 additions and 41 deletions

View File

@ -11,6 +11,7 @@ public class MsAssertionType {
public final static String TEXT = "Text";
public final static String XPATH2 = "XPath2";
private boolean enable = true;
public String label;
private String type;
}

View File

@ -98,6 +98,15 @@ public class MsHashTreeService {
private static final String RESULT_VARIABLE = "resultVariable";
private static final String ENV_Id = "environmentId";
private final static String JSON_PATH="jsonPath";
private final static String JSR223="jsr223";
private final static String XPATH="xpath2";
private final static String REGEX="regex";
private final static String DURATION="duration";
private final static String DOCUMENT="document";
private final static String LABEL="label";
private final static String SCENARIO_REF="SCENARIO-REF-STEP";
public void setHashTree(JSONArray hashTree) {
// 将引用转成复制
if (hashTree == null) {
@ -191,7 +200,7 @@ public class MsHashTreeService {
List<JSONObject> pre = ElementUtil.mergeHashTree(groupMap.get(PRE), targetGroupMap.get(PRE));
List<JSONObject> post = ElementUtil.mergeHashTree(groupMap.get(POST), targetGroupMap.get(POST));
List<JSONObject> rules = ElementUtil.mergeHashTree(groupMap.get(ASSERTIONS), targetGroupMap.get(ASSERTIONS));
List<JSONObject> rules = mergeAssertions(groupMap.get(ASSERTIONS), targetGroupMap.get(ASSERTIONS));
List<JSONObject> step = new LinkedList<>();
if (CollectionUtils.isNotEmpty(pre)) {
step.addAll(pre);
@ -232,6 +241,68 @@ public class MsHashTreeService {
return element;
}
public List<JSONObject> mergeAssertions(List<JSONObject> sourceHashTree, List<JSONObject> targetHashTree) {
try {
if (CollectionUtils.isNotEmpty(targetHashTree)) {
JSONObject target = targetHashTree.get(0);
if (CollectionUtils.isNotEmpty(sourceHashTree)) {
JSONObject source = sourceHashTree.get(0);
//jsonPath
JSONArray jsonPathTar = target.optJSONArray(JSON_PATH);
JSONArray jsonPathSource = source.optJSONArray(JSON_PATH);
mergeArray(target, jsonPathTar, jsonPathSource, JSON_PATH);
//jsr223
JSONArray jsr223Tar = target.optJSONArray(JSR223);
JSONArray jsr223Source = source.optJSONArray(JSR223);
mergeArray(target, jsr223Tar, jsr223Source, JSR223);
//xpath
JSONArray xpathTar = target.optJSONArray(XPATH);
JSONArray xpathSource = source.optJSONArray(XPATH);
mergeArray(target, xpathTar, xpathSource, XPATH);
//regex
JSONArray regexTar = target.optJSONArray(REGEX);
JSONArray regexSource = source.optJSONArray(REGEX);
mergeArray(target, regexTar, regexSource, REGEX);
//duration
JSONObject durationTar = target.optJSONObject(DURATION);
JSONObject durationSource = source.optJSONObject(DURATION);
mergeObject(target, durationTar, durationSource, DURATION);
//document
JSONObject documentTar = target.optJSONObject(DOCUMENT);
JSONObject documentSource = source.optJSONObject(DOCUMENT);
mergeObject(target, documentTar, documentSource, DOCUMENT);
sourceHashTree.remove(0);
sourceHashTree.add(target);
}
}
} catch (Exception e) {
LogUtil.error("mergeAssertions error", e);
}
return sourceHashTree;
}
private static void mergeObject(JSONObject target, JSONObject durationTar, JSONObject durationSource, String type) {
if (durationSource != null && durationSource.has(LABEL) &&
StringUtils.equals(durationSource.optString(LABEL), SCENARIO_REF)) {
durationTar = durationSource;
}
target.remove(type);
target.put(type, durationTar);
}
private static void mergeArray(JSONObject target, JSONArray jsonArray, JSONArray source, String type) {
if (!source.isEmpty()) {
source.forEach(obj -> {
JSONObject jsonObject = (JSONObject) obj;
if (StringUtils.equals(jsonObject.optString(LABEL), SCENARIO_REF)) {
jsonArray.put(jsonObject);
}
});
}
target.remove(type);
target.put(type, jsonArray);
}
private void getCaseIds(JSONObject element, List<String> caseIds) {
if (StringUtils.equalsIgnoreCase(element.optString(REF_TYPE), CASE) && element.has(ID)) {

View File

@ -3,7 +3,7 @@
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
<el-col>
<el-input
:disabled="isReadOnly"
:disabled="isReadOnly && !duration.label"
:value="value"
v-bind="$attrs"
step="100"
@ -21,18 +21,18 @@
v-model="duration.enable"
class="enable-switch"
size="mini"
:disabled="isReadOnly"
:disabled="isReadOnly && !duration.label"
style="width: 30px; margin-right: 10px" />
</el-tooltip>
<el-button
:disabled="isReadOnly"
:disabled="isReadOnly && !duration.label"
type="danger"
size="mini"
icon="el-icon-delete"
circle
@click="remove"
v-if="edit" />
<el-button :disabled="isReadOnly" type="primary" size="mini" @click="add" v-else>
<el-button :disabled="isReadOnly && !duration.label" type="primary" size="mini" @click="add" v-else>
{{ $t('api_test.request.assertions.add') }}
</el-button>
</el-col>
@ -55,6 +55,11 @@ export default {
},
},
created() {
if (this.duration && !this.duration.valid && this.duration.value === 0 && this.isReadOnly) {
this.duration.label = "SCENARIO-REF-STEP";
}
},
methods: {
add() {
if (this.validate()) {

View File

@ -4,7 +4,7 @@
<el-col>
<el-tooltip :disabled="showTip" placement="top" :content="jsonPath.expression">
<el-input
:disabled="isReadOnly"
:disabled="isReadOnly && !jsonPath.label"
v-model="jsonPath.expression"
maxlength="500"
size="small"
@ -14,7 +14,7 @@
</el-col>
<el-col>
<el-select
:disabled="isReadOnly"
:disabled="isReadOnly && !jsonPath.label"
v-model="jsonPath.option"
class="ms-col-type"
size="small"
@ -29,7 +29,7 @@
<el-option :label="$t('api_test.request.assertions.regular_match')" value="REGEX"/>
</el-select>
<el-input
:disabled="isReadOnly"
:disabled="isReadOnly && !jsonPath.label"
v-model="jsonPath.expect"
size="small"
show-word-limit
@ -48,25 +48,25 @@
v-model="jsonPath.enable"
class="enable-switch"
size="mini"
:disabled="isReadOnly"
:disabled="isReadOnly && !jsonPath.label"
style="width: 30px; margin-right: 10px"/>
</el-tooltip>
<el-button
:disabled="isReadOnly"
:disabled="isReadOnly && !jsonPath.label"
size="mini"
icon="el-icon-copy-document"
circle
@click="copyRow"
v-if="edit"/>
<el-button
:disabled="isReadOnly"
:disabled="isReadOnly && !jsonPath.label"
type="danger"
size="mini"
icon="el-icon-delete"
circle
@click="remove"
v-if="edit"/>
<el-button :disabled="isReadOnly" type="primary" size="mini" @click="add" v-else>
<el-button :disabled="isReadOnly && !jsonPath.label" type="primary" size="mini" @click="add" v-else>
{{ $t('api_test.request.assertions.add') }}
</el-button>
</el-col>
@ -102,6 +102,9 @@ export default {
created() {
if (!this.jsonPath.option) {
this.jsonPath.option = 'REGEX';
if (this.jsonPath && this.isReadOnly) {
this.jsonPath.label = "SCENARIO-REF-STEP";
}
}
this.showTip = !this.jsonPath || !this.jsonPath.expression || this.jsonPath.expression.length < 50;
},

View File

@ -5,10 +5,10 @@
{{ assertion.desc }}
</div>
<div class="assertion-item btn">
<el-button :disabled="isReadOnly" type="success" size="mini" @click="detail">
<el-button :disabled="isReadOnly && !assertion.label" type="success" size="mini" @click="detail">
{{ $t('commons.edit') }}
</el-button>
<el-button :disabled="isReadOnly" type="primary" size="mini" @click="add">
<el-button :disabled="isReadOnly && !assertion.label" type="primary" size="mini" @click="add">
{{ $t('api_test.request.assertions.add') }}
</el-button>
</div>
@ -19,16 +19,16 @@
</div>
<div class="assertion-item btn circle">
<i class="el-icon-view el-button el-button--primary el-button--mini is-circle" circle @click="showPage" />
<el-button :disabled="isReadOnly" type="success" size="mini" icon="el-icon-edit" circle @click="detail" />
<el-button :disabled="isReadOnly && !assertion.label" type="success" size="mini" icon="el-icon-edit" circle @click="detail" />
<el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top">
<el-switch
v-model="assertion.enable"
class="enable-switch"
size="mini"
:disabled="isReadOnly"
:disabled="isReadOnly && !assertion.label"
style="width: 30px; margin: 0px 10px 0px 10px" />
</el-tooltip>
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove" />
<el-button :disabled="isReadOnly && !assertion.label" type="danger" size="mini" icon="el-icon-delete" circle @click="remove" />
</div>
</el-row>
@ -167,6 +167,11 @@ export default {
};
},
created() {
if (this.assertion.valid === undefined && this.isReadOnly) {
this.assertion.label = "SCENARIO-REF-STEP";
}
},
methods: {
add() {
this.list.push(new AssertionJSR223(this.assertion));

View File

@ -3,7 +3,7 @@
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
<el-col class="assertion-select">
<el-select
:disabled="isReadOnly"
:disabled="isReadOnly && !regex.label"
class="assertion-item"
v-model="regex.subject"
size="small"
@ -16,7 +16,7 @@
<el-col>
<el-tooltip :disabled="showTip" placement="top" :content="regex.expression">
<el-input
:disabled="isReadOnly"
:disabled="isReadOnly && !regex.label"
v-model="regex.expression"
size="small"
show-word-limit
@ -24,7 +24,7 @@
</el-tooltip>
</el-col>
<el-col class="assertion-checkbox">
<el-checkbox v-model="regex.assumeSuccess" :disabled="isReadOnly">
<el-checkbox v-model="regex.assumeSuccess" :disabled="isReadOnly && !regex.label">
{{ $t('api_test.request.assertions.ignore_status') }}
</el-checkbox>
</el-col>
@ -34,26 +34,26 @@
v-model="regex.enable"
class="enable-switch"
size="mini"
:disabled="isReadOnly"
:disabled="isReadOnly && !regex.label"
style="width: 30px; margin-right: 10px" />
</el-tooltip>
<el-button
:disabled="isReadOnly"
:disabled="isReadOnly && !regex.label"
size="mini"
icon="el-icon-copy-document"
circle
@click="copyRow"
v-if="edit" />
<el-button
:disabled="isReadOnly"
:disabled="isReadOnly && !regex.label"
type="danger"
size="mini"
icon="el-icon-delete"
circle
@click="remove"
v-if="edit" />
<el-button :disabled="isReadOnly" type="primary" size="mini" @click="add" v-else>
<el-button :disabled="isReadOnly && !regex.label" type="primary" size="mini" @click="add" v-else>
{{ $t('api_test.request.assertions.add') }}
</el-button>
</el-col>
@ -102,6 +102,9 @@ export default {
},
},
created() {
if (!this.regex.description && this.isReadOnly) {
this.regex.label = "SCENARIO-REF-STEP";
}
this.showTip = !this.regex || !this.regex.description || this.regex.description.length < 80;
},
methods: {

View File

@ -3,7 +3,7 @@
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
<el-col class="assertion-select">
<el-select
:disabled="isReadOnly"
:disabled="isReadOnly && !label"
class="assertion-item"
v-model="subject"
size="small"
@ -15,7 +15,7 @@
</el-col>
<el-col class="assertion-select">
<el-select
:disabled="isReadOnly"
:disabled="isReadOnly && !label"
class="assertion-item"
v-model="condition"
size="small"
@ -29,7 +29,7 @@
</el-col>
<el-col>
<el-input
:disabled="isReadOnly"
:disabled="isReadOnly && !label"
v-model="value"
maxlength="500"
size="small"
@ -37,12 +37,12 @@
:placeholder="$t('api_test.request.assertions.value')" />
</el-col>
<el-col class="assertion-checkbox">
<el-checkbox v-model="assumeSuccess" :disabled="isReadOnly">
<el-checkbox v-model="assumeSuccess" :disabled="isReadOnly && !label">
{{ $t('api_test.request.assertions.ignore_status') }}
</el-checkbox>
</el-col>
<el-col class="assertion-btn">
<el-button :disabled="isReadOnly" type="primary" size="mini" @click="add">
<el-button :disabled="isReadOnly && !label" type="primary" size="mini" @click="add">
{{ $t('api_test.request.assertions.add') }}
</el-button>
</el-col>
@ -72,8 +72,14 @@ export default {
condition: '',
assumeSuccess: false,
value: '',
label: '',
};
},
created() {
if (this.isReadOnly) {
this.label = 'SCENARIO-REF-STEP';
}
},
methods: {
add: function () {
@ -110,6 +116,7 @@ export default {
expression: expression,
description: description,
assumeSuccess: this.assumeSuccess,
label: this.label,
});
},
},

View File

@ -3,7 +3,7 @@
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
<el-col>
<el-input
:disabled="isReadOnly"
:disabled="isReadOnly && !xPath2.label"
v-model="xPath2.expression"
maxlength="500"
size="small"
@ -16,25 +16,25 @@
v-model="xPath2.enable"
class="enable-switch"
size="mini"
:disabled="isReadOnly"
:disabled="isReadOnly && !xPath2.label"
style="width: 30px; margin-right: 10px" />
</el-tooltip>
<el-button
:disabled="isReadOnly"
:disabled="isReadOnly && !xPath2.label"
size="mini"
icon="el-icon-copy-document"
circle
@click="copyRow"
v-if="edit" />
<el-button
:disabled="isReadOnly"
:disabled="isReadOnly && !xPath2.label"
type="danger"
size="mini"
icon="el-icon-delete"
circle
@click="remove"
v-if="edit" />
<el-button :disabled="isReadOnly" type="primary" size="mini" @click="add" v-else>
<el-button :disabled="isReadOnly && !xPath2.label" type="primary" size="mini" @click="add" v-else>
{{ $t('api_test.request.assertions.add') }}
</el-button>
</el-col>
@ -67,6 +67,12 @@ export default {
},
},
created() {
if (this.xPath2.valid === undefined && this.isReadOnly) {
this.xPath2.label = "SCENARIO-REF-STEP";
}
},
methods: {
add: function () {
this.list.push(this.getXPath2());

View File

@ -6,7 +6,6 @@
<api-json-path-suggest-button
:open-tip="$t('api_test.request.assertions.json_path_suggest')"
:clear-tip="$t('api_test.request.assertions.json_path_clear')"
:isReadOnly="isReadOnly"
@open="suggestJsonOpen"
@clear="clearJson" />
</span>
@ -15,7 +14,6 @@
<el-row :gutter="10">
<el-col :span="4">
<el-select
:disabled="isReadOnly"
class="assertion-item"
v-model="type"
:placeholder="$t('api_test.request.assertions.select_type')"

View File

@ -68,7 +68,7 @@
v-model="assertions.document.enable"
class="enable-switch"
size="mini"
:disabled="assertions.disabled"
:disabled="isReadOnly && !assertions.document.label"
style="width: 30px; margin-right: 10px" />
</el-tooltip>
<el-tooltip effect="dark" :content="$t('commons.remove')" placement="top-start">
@ -78,12 +78,12 @@
size="mini"
circle
@click="remove()"
:disabled="assertions.disabled && !assertions.root" />
:disabled="isReadOnly && !assertions.document.label && !assertions.root" />
</el-tooltip>
</el-col>
</el-row>
</div>
<ms-document-body :document="assertions.document" :apiId="apiId" :isReadOnly="isReadOnly" @remove="remove" />
<ms-document-body :document="assertions.document" :apiId="apiId" :isReadOnly="isReadOnly && !assertions.document.label" @remove="remove" />
</div>
</div>
</template>

View File

@ -8,7 +8,7 @@
</el-select>
</el-col>
<el-col class="assertion-btn">
<el-button :disabled="isReadOnly" type="primary" size="mini" @click="add">
<el-button :disabled="isReadOnly && !document.label" type="primary" size="mini" @click="add">
{{ $t('api_test.request.assertions.add') }}
</el-button>
</el-col>
@ -32,6 +32,11 @@ export default {
default: false,
},
},
created() {
if (this.document && !this.document.originalData && !this.document.rootData && this.isReadOnly) {
this.document.label = "SCENARIO-REF-STEP";
}
},
methods: {
add() {

View File

@ -114,7 +114,7 @@
:request="request"
:apiId="apiId"
:draggable="true"
:is-read-only="data.disabled"
:is-read-only="request.disabled"
:assertions="data" />
</div>
</span>