MeterSphere/frontend/src/business/components/track/common/minder/TestCaseMinder.vue

569 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<ms-module-minder
v-loading="result.loading"
minder-key="TEST_CASE"
:tree-nodes="treeNodes"
:tags="tags"
:select-node="selectNode"
:distinct-tags="tags"
:module-disable="false"
:show-module-tag="true"
:move-enable="moveEnable"
:tag-edit-check="tagEditCheck()"
:priority-disable-check="priorityDisableCheck()"
:disabled="disabled"
:get-extra-node-count="getMinderTreeExtraNodeCount()"
@afterMount="handleAfterMount"
@save="save"
ref="minder"
/>
<IssueRelateList
:case-id="getCurCaseId()"
@refresh="refreshRelateIssue"
ref="issueRelate"/>
<test-plan-issue-edit
:is-minder="true"
:plan-id="null"
:case-id="getCurCaseId()"
@refresh="refreshIssue"
ref="issueEdit"/>
</div>
</template>
<script>
import MsModuleMinder from "@/business/components/common/components/MsModuleMinder";
import {
getChildNodeId,
handleAfterSave,
handleExpandToLevel,
handleMinderIssueDelete,
handleTestCaseAdd,
handTestCaeEdit,
isCaseNodeData,
isModuleNode,
isModuleNodeData,
listenBeforeExecCommand,
listenDblclick,
listenNodeSelected,
loadSelectNodes,
priorityDisableCheck,
tagEditCheck,
} from "@/business/components/track/common/minder/minderUtils";
import {getNodePath, getUUID, hasPermission} from "@/common/js/utils";
import {getTestCasesForMinder, getMinderExtraNode, getMinderTreeExtraNodeCount} from "@/network/testCase";
import {addIssueHotBox, getSelectedNodeData, handleIssueAdd, handleIssueBatch} from "./minderUtils";
import IssueRelateList from "@/business/components/track/case/components/IssueRelateList";
import TestPlanIssueEdit from "@/business/components/track/case/components/TestPlanIssueEdit";
const {getIssuesListById} = require("@/network/Issue");
const {getCurrentWorkspaceId} = require("@/common/js/utils");
export default {
name: "TestCaseMinder",
components: {TestPlanIssueEdit, IssueRelateList, MsModuleMinder},
data() {
return{
testCase: [],
dataMap: new Map(),
tags: [this.$t('api_test.definition.request.case'), this.$t('test_track.case.prerequisite'), this.$t('commons.remark'), this.$t('test_track.module.module')],
result: {loading: false},
needRefresh: false,
saveCases: [],
saveModules: [],
saveModuleNodeMap: new Map(),
deleteNodes: [], // 包含测试模块、用例和临时节点
saveExtraNode: {},
extraNodeChanged: [] // 记录转成用例或模块的临时节点
}
},
props: {
treeNodes: {
type: Array,
default() {
return []
}
},
currentVersion: String,
condition: Object,
projectId: String,
},
computed: {
selectNodeIds() {
return this.$store.state.testCaseSelectNodeIds;
},
selectNode() {
return this.$store.state.testCaseSelectNode;
},
moduleOptions() {
return this.$store.state.testCaseModuleOptions;
},
testCaseDefaultValue() {
return this.$store.state.testCaseDefaultValue;
},
disabled() {
return !hasPermission('PROJECT_TRACK_CASE:READ+EDIT');
},
moveEnable() {
// 如果不是默认的排序条件不能调换位置
return !this.condition.orders || this.condition.orders.length < 1;
},
workspaceId(){
return getCurrentWorkspaceId();
}
},
watch: {
selectNode() {
if (this.$refs.minder) {
this.$refs.minder.handleNodeSelect(this.selectNode);
}
},
currentVersion() {
this.$refs.minder.initData();
}
},
mounted() {
this.setIsChange(false);
if (this.selectNode && this.selectNode.data) {
if (this.$refs.minder) {
let importJson = this.$refs.minder.getImportJsonBySelectNode(this.selectNode.data);
this.$refs.minder.setJsonImport(importJson);
}
}
},
methods: {
getMinderTreeExtraNodeCount() {
return getMinderTreeExtraNodeCount;
},
handleAfterMount() {
listenNodeSelected(() => {
// 点击模块,加载模块下的用例
loadSelectNodes(this.getParam(), getTestCasesForMinder, null, getMinderExtraNode);
});
listenDblclick(() => {
let minder = window.minder;
let selectNodes = minder.getSelectedNodes();
let isNotDisableNode = false;
selectNodes.forEach(node => {
// 如果鼠标双击了非模块的节点,表示已经编辑
if (!node.data.disable) {
isNotDisableNode = true;
}
if (node.data.type === 'issue') {
getIssuesListById(node.data.id, this.projectId,this.workspaceId,(data) => {
data.customFields = JSON.parse(data.customFields);
this.$refs.issueEdit.open(data);
});
}
});
if (isNotDisableNode) {
this.setIsChange(true);
}
});
listenBeforeExecCommand((even) => {
if (even.commandName === 'expandtolevel') {
let level = Number.parseInt(even.commandArgs);
handleExpandToLevel(level, even.minder.getRoot(), this.getParam(), getTestCasesForMinder);
}
if (handleMinderIssueDelete(even.commandName)) return; // 删除缺陷不算有编辑脑图信息
if (['priority', 'resource', 'removenode', 'appendchildnode', 'appendparentnode', 'appendsiblingnode'].indexOf(even.commandName) > -1) {
// 这些情况则脑图有改变
this.setIsChange(true);
}
});
addIssueHotBox(this);
},
getParam() {
return {
request: {
projectId: this.projectId,
versionId: this.currentVersion,
orders: this.condition.orders
},
result: this.result,
isDisable: false
}
},
setIsChange(isChanged) {
this.$store.commit('setIsTestCaseMinderChanged', isChanged);
},
save() {
this.saveCases = [];
this.saveModules = [];
this.deleteNodes = []; // 包含测试模块、用例和临时节点
this.saveExtraNode = {};
this.saveModuleNodeMap = new Map();
this.buildSaveParam(window.minder.getRoot());
this.saveModules.forEach(module => {
let nodeIds = [];
getChildNodeId(this.saveModuleNodeMap.get(module.id), nodeIds);
module.nodeIds = nodeIds;
});
let param = {
projectId: this.projectId,
data: this.saveCases,
ids: this.deleteNodes.map(item => item.id),
testCaseNodes: this.saveModules,
extraNodeRequest: {
groupId: this.projectId,
type: "TEST_CASE",
data: this.saveExtraNode,
}
}
this.result = this.$post('/test/case/minder/edit', param, () => {
this.$success(this.$t('commons.save_success'));
handleAfterSave(window.minder.getRoot());
this.extraNodeChanged.forEach(item => {
item.isExtraNode = false;
});
this.extraNodeChanged = [];
this.$emit('refresh');
this.setIsChange(false);
});
},
buildSaveParam(root, parent, preNode, nextNode) {
let data = root.data;
if (isCaseNodeData(data)) {
this.buildSaveCase(root, parent, preNode, nextNode);
} else {
let deleteChild = data.deleteChild;
if (deleteChild && deleteChild.length > 0 && isModuleNodeData(data)) {
this.deleteNodes.push(...deleteChild);
}
if (data.type !== 'tmp' && data.changed) {
if (isModuleNodeData(data)) {
if (data.contextChanged) {
this.buildSaveModules(root, data, parent);
root.children && root.children.forEach(i => {
if (isModuleNode(i)) {
i.data.changed = true;
i.data.contextChanged = true; // 如果当前节点有变化下面的模块节点也需要level也可能需要变化
}
});
}
} else {
// 保存临时节点
this.buildExtraNode(data, parent, root);
}
}
if (root.children) {
for (let i = 0; i < root.children.length; i++) {
let childNode = root.children[i];
let preNodeTmp = null;
let nextNodeTmp = null;
if (i != 0) {
preNodeTmp = root.children[i - 1];
}
if (i + 1 < root.children.length) {
nextNodeTmp = root.children[i + 1];
}
this.buildSaveParam(childNode, root.data, preNodeTmp, nextNodeTmp);
}
}
}
},
buildSaveModules(node, data, parent) {
if (!data.text) {
return;
}
let pId = parent ? (parent.newId ? parent.newId : parent.id) : null;
if (parent && !isModuleNodeData(parent)) {
this.throwError(this.$t('test_track.case.minder_not_module_tip', [data.text]));
}
let module = {
id: data.id,
name: data.text,
level: parent ? parent.level + 1 : data.level,
parentId: pId
};
data.level = module.level;
if (data.isExtraNode) {
// 如果是临时节点,打上了模块标签,则删除临时节点并新建模块
this.pushDeleteNode(data);
module.id = null;
this.extraNodeChanged.push(data);
}
if (data.type === 'case') {
// 如果是用例节点,打上了模块标签,则用例节点并新建模块
this.pushDeleteNode(data);
module.id = null;
}
if (module.id && module.id.length > 20) {
module.isEdit = true; // 编辑
} else {
module.isEdit = false; // 新增
module.id = getUUID();
data.newId = module.id;
this.moduleOptions.push({id: data.newId, path: getNodePath(pId, this.moduleOptions) + '/' + module.name});
}
this.saveModuleNodeMap.set(module.id, node);
if (module.level > 8) {
this.throwError(this.$t('commons.module_deep_limit'));
}
this.saveModules.push(module);
},
buildExtraNode(data, parent, root) {
if (data.type !== 'node' && data.type !== 'tmp' && parent && isModuleNodeData(parent) && data.changed === true) {
// 保存额外信息,只保存模块下的一级子节点
let nodes = this.saveExtraNode[parent.id];
if (!nodes) {
nodes = [];
}
nodes.push(JSON.stringify(this._buildExtraNode(root)));
this.saveExtraNode[parent.newId ? parent.newId : parent.id] = nodes;
}
},
validate(parent, data) {
if (parent.id === 'root') {
this.throwError(this.$t('test_track.case.minder_all_module_tip'));
}
if (parent.isExtraNode && !isModuleNodeData(parent)) {
this.throwError(this.$t('test_track.case.minder_tem_node_tip', [parent.text]));
}
if (data.type === 'node') {
this.throwError(this.$t('test_track.case.minder_is_module_tip', [data.text]));
}
},
buildSaveCase(node, parent, preNode, nextNode) {
let data = node.data;
if (!data.text) {
return;
}
this.validate(parent, data);
let isChange = false;
let nodeId = parent ? (parent.newId ? parent.newId : parent.id) : "";
let priorityDefaultValue;
if (data.priority ) {
priorityDefaultValue = 'P' + (data.priority - 1);
} else {
priorityDefaultValue = this.testCaseDefaultValue['用例等级'] ? this.testCaseDefaultValue['用例等级'] : 'P' + 0;
}
let testCase = {
id: data.id,
name: data.text,
nodeId: nodeId,
nodePath: getNodePath(nodeId, this.moduleOptions),
type: data.type ? data.type : 'functional',
method: data.method ? data.method : 'manual',
maintainer: this.testCaseDefaultValue['责任人'] ? this.testCaseDefaultValue['责任人'] : data.maintainer,
priority: priorityDefaultValue,
prerequisite: "",
remark: "",
stepDescription: "",
expectedResult: "",
status: this.testCaseDefaultValue['用例状态'],
steps: [{
num: 1,
desc: '',
result: ''
}],
tags: "[]",
stepModel: "STEP"
};
if (data.changed) isChange = true;
let steps = [];
let stepNum = 1;
if (node.children) {
node.children.forEach((childNode) => {
let childData = childNode.data;
if (childData.type === 'issue') return;
if (childData.resource && childData.resource.indexOf(this.$t('test_track.case.prerequisite')) > -1) {
testCase.prerequisite = childData.text;
if (childNode.children && childNode.children.length > 0) {
this.throwError('[' + testCase.name + ']前置条件下不能添加子节点!');
}
} else if (childData.resource && childData.resource.indexOf(this.$t('commons.remark')) > -1) {
testCase.remark = childData.text;
if (childNode.children && childNode.children.length > 0) {
this.throwError('[' + testCase.name + ']备注下不能添加子节点!');
}
} else {
// 测试步骤
let step = {};
step.num = stepNum++;
step.desc = childData.text;
if (childNode.children) {
let result = "";
childNode.children.forEach((child) => {
result += child.data.text;
if (child.data.changed) {
isChange = true;
}
})
step.result = result;
}
steps.push(step);
if (data.stepModel === 'TEXT') {
testCase.stepDescription = step.desc;
testCase.expectedResult = step.result;
}
}
if (childData.changed) {
isChange = true;
}
childNode.children.forEach((child) => {
if (child.children && child.children.length > 0) {
this.throwError('['+ testCase.name + ']用例下子节点不能超过两层!');
}
});
})
}
testCase.steps = JSON.stringify(steps);
if (data.isExtraNode) {
// 如果是临时节点,打上了用例标签,则删除临时节点并新建用例节点
this.pushDeleteNode(data);
testCase.id = null;
this.extraNodeChanged.push(data);
}
if (isChange) {
testCase.targetId = null; // 排序处理
if (this.moveEnable) {
if (this.isCaseNode(preNode)) {
let preId = preNode.data.id;
if (preId && preId.length > 15) {
testCase.targetId = preId;
testCase.moveMode = 'AFTER';
} else {
testCase.moveMode = 'APPEND';
}
} else if (this.isCaseNode(nextNode)) {
let nextId = nextNode.data.id;
if (nextId && nextId.length > 15) {
testCase.targetId = nextId;
testCase.moveMode = 'BEFORE';
}
}
}
if (testCase.id && testCase.id.length > 20) {
testCase.isEdit = true; // 编辑
} else {
testCase.isEdit = false; // 新增
testCase.id = getUUID();
data.newId = testCase.id;
}
this.saveCases.push(testCase);
}
if (testCase.nodeId !== 'root' && testCase.nodeId.length < 15) {
this.throwError(this.$t('test_track.case.create_case') + "'" + testCase.name + "'" + this.$t('test_track.case.minder_create_tip'));
}
},
pushDeleteNode(data) {
// 如果是临时节点,打上了用例标签,则删除临时节点并新建用例节点
let deleteData = {};
Object.assign(deleteData, data);
this.deleteNodes.push(deleteData);
data.id = "";
},
isCaseNode(node) {
if (node && node.data.resource && node.data.resource.indexOf(this.$t('api_test.definition.request.case')) > -1) {
return true;
}
return false;
},
_buildExtraNode(node) {
let data = node.data;
let nodeData = {
text: data.text,
id: data.id,
resource: data.resource,
};
if (nodeData.id && nodeData.id.length > 20) {
nodeData.isEdit = true; // 编辑
} else {
nodeData.isEdit = false; // 新增
nodeData.id = getUUID();
data.newId = nodeData.id;
}
if (node.children) {
nodeData.children = [];
node.children.forEach(item => {
nodeData.children.push(this._buildExtraNode(item));
});
}
data.isExtraNode = true;
return nodeData;
},
throwError(tip) {
this.$error(tip)
throw new Error(tip);
},
tagEditCheck() {
return tagEditCheck;
},
priorityDisableCheck() {
return priorityDisableCheck;
},
// 打开脑图之后添加新增或修改tab页时同步修改脑图
addCase(data, type) {
if (type === 'edit') {
handTestCaeEdit(data);
} else {
handleTestCaseAdd(data.nodeId, data);
}
this.needRefresh = true;
},
refresh() {
// 切换tab页如果没有修改用例不刷新脑图
if (this.needRefresh) {
let jsonImport = window.minder.exportJson();
this.$refs.minder.setJsonImport(jsonImport);
this.$nextTick(() => {
if (this.$refs.minder) {
this.$refs.minder.reload();
}
});
this.needRefresh = false;
}
},
getCurCaseId() {
return getSelectedNodeData().id;
},
refreshIssue(issue) {
handleIssueAdd(issue);
},
refreshRelateIssue(issues) {
handleIssueBatch(issues);
}
}
}
</script>
<style scoped>
</style>